Repository navigation
forms: a field whose defaultValue is a CEL expression opens empty on a new record with no sign that the server will fill it on save #12108
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:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchand removed
on Oct 10, 2026 objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-11T04:33Z
Session:session_01AswpQDLCKiZos2jCXknwKz
Account:os-zhuang
Branch:claude/issue-12108-cel-default-hint
Worktree:objectui-issue-12108
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/plugin-form/src/schemaDefaults.ts(isSeedableDefault,schemaDefaultValues); the field rendering that draws the "set on save" hint (named in the PR, inplugin-formorfields); the required-field Save check if the measurement shows it refuses; locale entries inpackages/i18n/src/locales/for the hint (all ten packs); their tests;.changeset/12108-cel-default-hint.md(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"
Clause-②: yes
Responsibility:objectui's plugin-form schemaDefaults.ts, which deliberately leaves a CEL or runtime-token default unseeded, so the field opens empty with nothing saying the server will fill it on save | none: the server resolves the default at insert, and the form says nothing about it | every author whose field declares a CEL defaultValue, and every user who opens that object's create form; measured on objectstack-ai/hotcrm#2058 (17.7.0)
Thread-read: none
Serial constraints cleared: none in flight on these files. Read at 2026-10-11T04:33Z: objectui#12116 (in the merge queue) touchesplugin-listandplugin-calendar; objectui#12118 (objectui#12104) touchesplugin-timeline; this seat's in-flight branch for objectui#12105 touchesplugin-gridandapp-shell's action params; objectui#12089 (the objectui#7611 epic) touches noplugin-formsource.Why
Clause-②: yes: the hint's copy adds a published locale key, which widensTranslationKeys. If the measurement also changes the required-field check, the report says so.
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 12108,
"status": "done",
"branch": "claude/issue-12108-cel-default-hint",
"pr": "#12120",
"session": "session_01AswpQDLCKiZos2jCXknwKz",
"premise_still_valid": true,
"summary": "Measured first: on main 5f75cfa a CEL envelope or runtime-token defaultValue is left unseeded and omitted from the create payload, and nothing in any layout said the server fills it (premise holds). The hint lands in the form renderer, packages/components/src/renderers/form/form.tsx (renderFormField, via a module-private renderFieldDescription), not in plugin-form or fields: registered widgets read their placeholder off field metadata and the date/time/boolean/user widgets draw none, while the renderer is the one draw site all six containers share and already computes isServerOwnedValue per field. 'Set automatically when saved.' (form.serverDefaultHint, ten packs) shows in the field's FormDescription exactly while serverOwned (same isServerOwnedValue answer the required suppression reads, computed once) and isMissingForRequired(value) hold, and not on a disabled or read-only control, which keeps it off view-mode forms the renderer reads as create. The required-field measurement found no defect: a required date or select with a CEL default submits empty, unmarked and omitted in six routes, and objectstack's insert path resolves defaults before validating, so it is pinned as behaviour, not changed.",
"tests": "HEAD 662a0ba. New pin packages/plugin-form/src/tests/serverDefaultHint-12108.test.tsx: 34 passed (7 ObjectForm routes x hint/CONTROL/select-placeholder; live-value, aria-describedby, description order; edit and view mode; required date+select submit in 6 routes). | pnpm exec vitest run --maxWorkers=2 packages/plugin-form/ -> Test Files 173 passed (173), Tests 2127 passed | 1 skipped (2128) at 662a0ba. | pnpm exec vitest run --maxWorkers=2 packages/components/src/renderers/form/ -> Test Files 71 passed (71), Tests 495 passed | 17 skipped (512) at 662a0ba. | pnpm exec vitest run --maxWorkers=2 packages/i18n/ -> Test Files 82 passed (82), Tests 1316 passed | 13 skipped (1329) at 662a0ba. | pnpm exec vitest run --maxWorkers=2 packages/core/src/validation/ -> Test Files 9 passed (9), Tests 152 passed (152) at 662a0ba. | Ablation at 662a0ba via objectstack scripts/ablation-replace.mjs (wrap mode, anchor x1 -> x0, blob changed; restore: blob after == HEAD blob 7996a31dd3d6, git diff HEAD empty) plus a shell trap on EXIT INT TERM: hint text removed at the draw site -> 17 failed | 17 passed; disabled gate removed -> 2 failed (both view-mode pins); isMissingForRequired check removed -> 1 failed (typed-value pin). Same three readings at 26c889e. No rebuild needed: vitest aliases @object-ui/components to packages/components/src (vitest.config.mts), so the mutated source is what ran. | First run of the pin (before commit 26c889e) also carried a boolean CEL-default field: 13 failed, all on that field, payload held active: false. Boolean removed from the fixture; reported as a finding.",
"mcp_calls": "0 — no MCP tool called",
"api_writes": "3 REST writes as objectstack-fleet[bot] through 2 fleet-write relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, HTTP 204, x2): POST /repos/objectstack-ai/objectui/pulls (draft, objectui#12120), POST /repos//issues/12120/assignees (os-zhuang), POST /repos//issues/12108/comments (this report). Plus git push x4 to claude/issue-12108-cel-default-hint (not REST). 0 label writes.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: save from the create form, measured in-process through the real ObjectForm and ModalForm (the console create dialog) with registered widgets, not in a live browser; no named production producer found (objectstack examples declare no CEL defaultValue at all) · evidence: a boolean field declaring defaultValue { dialect: 'cel', source: 'true' } submits active: false on create (AssertionError: expected { status: 'open', active: false, …(1) } to not have property active), because form.tsx's defaultValues memo seeds false for every BOOLEAN_WIDGET_TYPES field nobody supplied (the cloud#972 seed) and omitServerResolvedDefaults keeps false, so ObjectQL.applyFieldDefaults (out[f.name] != null) never evaluates the declaration · Seam: spec:FieldSchema.defaultValue (CEL envelope) → runtime:ObjectQL.applyFieldDefaults | renderer:form.tsx defaultValues boolean seed · dedupe words: boolean CEL defaultValue create form, boolean false seed runtime default, cloud#972 server-owned boolean, applyFieldDefaults boolean false",
"carrier: 承接者:无 · noted, not filed — apps/console/src/components/FormPage.tsx (the console's second form renderer, /forms/:name and /f/:slug) omits server-owned blanks and lowers required on them but draws no 'set on save' hint; read, not measured; in PR Acceptance notes",
"carrier: 承接者:无 · noted, not filed — the containers pass previousValues in edit mode only, so the renderer reads a mode: 'view' form as create (server-owned-value.ts calls previousValues absence the declared insert signal, objectui#3484); no wrong answer observable today (view mode submits nothing, the new hint is gated off disabled controls); in PR body"
],
"gates": [
"pnpm --filter @object-ui/components type-check · exit 0 · '> tsc --noEmit && tsc -p tsconfig.test.json' then TC exit 0 · 662a0ba",
"pnpm --filter @object-ui/i18n type-check · exit 0 · ad59f8b",
"pnpm --filter @object-ui/core type-check · exit 0 · ad59f8b",
"pnpm --filter @object-ui/plugin-form type-check · exit 0 · ad59f8b (ad59f8b..662a0ba touches form.tsx only); tsc -p tsconfig.test.json --listFiles lists serverDefaultHint-12108.test.tsx (1 hit, control createDefaults.test.tsx 1 hit) at 662a0ba",
"pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-form^...' build · exit 0 · os-verify-lock VERDICT command-exit 0, 11 packages · ad59f8b",
"pnpm check:i18n-keys · exit 0 · 'Every in-scope call-site key resolves against the en pack (3324 keys)…' · 662a0ba",
"pnpm check:i18n-drift · exit 0 · 'No en value changed in this range.' · 662a0ba",
"pnpm check:i18n-dead-keys · exit 0 · report-only; form.serverDefaultHint 0 hits among candidates · 662a0ba",
"pnpm check:i18n-designer-parity · exit 0 · 'no zh-only key falls outside the families above.' · 662a0ba",
"pnpm check:eager-closure · exit 0 · 'Console eager closure is 3170.5 KB gzipped across 290 of 2474 chunks (budget: 3204.6 KB, headroom: 34.0 KB).' · 662a0ba; base 5f75cfa: 3170.4 KB, headroom 34.2 KB; delta eagerGzipBytes +128 (3246485 -> 3246613), raw +301, ui-components +110 B, i18n-locale-en +21 B, same 290 eager chunks",
"pnpm check:eager-locale-catalogues · exit 0 · 'Exactly one locale catalogue is eager — en, the resident fallback' · 662a0ba and base 5f75cfa (en 44.4 KB both)",
"pnpm check:new-line-citations · exit 0 · 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0' · 662a0ba",
"pnpm check:control-bytes · exit 0 · 'check-control-bytes: OK (scanned 8314 tracked text file(s); skipped 85 binary).' · 662a0ba",
"node scripts/check-changeset-presence.mjs · exit 0 · '14 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s): .changeset/12108-cel-default-hint.md.' · 662a0ba",
"node scripts/check-changeset-no-major.mjs · exit 0 · 'No changeset declares a major bump.' · 662a0ba",
"pnpm check:changeset-claims · exit 0 · 'No pending changeset names a file this change touches.' · 662a0ba",
"pnpm check:pending-changeset-literals · exit 0 · 'No test source names a pending changeset.' · 662a0ba",
"pnpm check:test-path-roots · exit 0 · 'check-test-path-roots: OK' · 662a0ba",
"pnpm check:vi-mock-specifiers · exit 0 · OK · 662a0ba",
"pnpm check:vi-mock-inherit · exit 0 · OK · 662a0ba",
"pnpm check:vi-mock-override-shape · exit 0 · OK · 662a0ba",
"pnpm check:esm-specifiers · exit 0 · 'Specifier leg: no un-ledgered package emits an extensionless relative specifier.' · 662a0ba",
"pnpm check:self-import · exit 0 · 'No package names itself inside its own src/.' · 662a0ba",
"pnpm check:phantom-deps · exit 0 · 'Every in-scope import is declared by the package that publishes it.' · 662a0ba",
"pnpm check:doc-types · exit 0 · 'Every documented component type is registered.' · 662a0ba",
"pnpm check:doc-example-ids · exit 0 · '415 real reference(s) all resolve in the catalog registry.' · 662a0ba",
"pnpm check:doc-fences · exit 0 · 'every TypeScript block in 227 document(s) is fenced' · 662a0ba",
"node scripts/check-doc-links.mjs · exit 0 · 'Links are valid across 17 scan roots.' · 662a0ba",
"pnpm check:doc-example-readers · exit 0 · 'OK 80 documented symbol(s)' · 662a0ba",
"eslint --no-inline-config --format json (14 touched lintable files) · exit 0 · 0 errors, 75 warnings; population: eslint --print-config resolves a config for all 14, 0 ignored; count: 14 files in the JSON; invariance: eslint.config.js has no parserOptions.project/projectService and no eslint-rules/ rule reads the disk; form.tsx 70 warnings = its base blob; new test 5 no-explicit-any · 662a0ba",
"NOT MEASURED: check:doc-snippets, check:doc-examples, reason: PREREQUISITE NOT MET (34 unbuilt packages); 0 of 33 changed doc lines inside a fence (detector control: 341 and 204 fenced lines) · 662a0ba",
"NOT MEASURED: check:readme-exports, reason: needs every package built; README diff touches no export listing",
"NOT MEASURED: repo-wide pnpm lint and full pnpm test (CI's); live browser pass; WizardForm required-field submit (its submit is the last-step button)"
],
"line_budget": "n/a — no skills/** path touched",
"deviations": [
"File surface: the hint landed in packages/components/src/renderers/form/form.tsx, outside the claim's surface (plugin-form or fields). The claim says 'stop on breach; explain in the report'; the dispatch says to fix at the real producer in another package and explain. I followed the dispatch and flag the conflict rather than pick a side silently. Also outside the claim's list: a comment-only edit to packages/core/src/validation/server-owned-value.ts (its consumer list), and docs (plugin-form README, content/docs guide) per AGENTS.md rule 2.",
"Attribution: commits end with AGENTS.md's model-free pair (Claude-Session URL + 'Co-authored-by: Claude' noreply) instead of the harness's model-named Co-Authored-By line, and the PR footer is os-dev's session-URL form, per those files' precedence over the harness reminder.",
"Stray file /tc.pid (6 bytes, a PID) created at the filesystem root by a shell variable scoped inside a backgrounded && chain; my rm was refused by a built-in safety check and was not retried. Left for the user to delete.",
"objectui has no scripts/ablation-replace.mjs; the ablation used objectstack's copy by absolute path, run from the objectui worktree.",
"objectui has no scripts/pm/dispatch-gates.mjs; the gate set was derived by hand from objectui's package.json and .github/workflows.",
"A comparison worktree objectui-issue-12108-cmp (detached at 5f75cfa, read-only) was created for the base eager build and removed; a scratch ref refs/issue12108/remote-head was fetched for the push check and deleted.",
"The README's six cross-file line addresses were re-cited by content (AGENTS.md rule 11, a touched file)."
],
"files_changed": [
".changeset/12108-cel-default-hint.md",
"content/docs/guide/building-crud-app.md",
"packages/components/src/renderers/form/form.tsx",
"packages/core/src/validation/server-owned-value.ts",
"packages/i18n/src/locales/ar.ts",
"packages/i18n/src/locales/de.ts",
"packages/i18n/src/locales/en.ts",
"packages/i18n/src/locales/es.ts",
"packages/i18n/src/locales/fr.ts",
"packages/i18n/src/locales/ja.ts",
"packages/i18n/src/locales/ko.ts",
"packages/i18n/src/locales/pt.ts",
"packages/i18n/src/locales/ru.ts",
"packages/i18n/src/locales/zh.ts",
"packages/plugin-form/README.md",
"packages/plugin-form/src/tests/serverDefaultHint-12108.test.tsx",
"packages/plugin-form/src/schemaDefaults.ts"
],
"clause_2_surface": "exports added: none; props added: none (renderFieldDescription is module-private); locale key added: form.serverDefaultHint in ten packs, TranslationKeys widens, @object-ui/i18n minor; DOM locator data-server-default-hint=true on the hint span"
}
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsACCEPT — PR objectui#12120 (head
662a0ba440), with the claim amendment ·domain:uiseat 1,session_01AswpQDLCKiZos2jCXknwKz, 2026-10-11T05:44ZReviewed against GitHub and the diff, not the report (
os-dev-reporton this card).Claim amendment (to claim
6105472984)- File surface, extended: the hint lands in
packages/components/src/renderers/form/form.tsx(renderFormField, through a module-privaterenderFieldDescription). The claim predictedplugin-formorfields, but this renderer is the one draw site all six object-form containers share, and it already computes the server-owned classifier per field. A placeholder could not reach the date, time, boolean or user widgets. - Also extended: a comment-only consumer-list edit in
packages/core/src/validation/server-owned-value.ts,packages/plugin-form/README.mdandcontent/docs/guide/building-crud-app.md(objectui AGENTS.md Add automated testing infrastructure and CI/CD workflows #2). - Serial constraint re-read for the new file: no open PR touches
packages/components/src/renderers/form/. objectui#12119 and feat(app-shell,fields,console): Setup's positions and permission sets read the registry, through the metadata-admin pages' environment scope (part of objectui#7611) #12089 do not, and neither does this seat's in-flight branch for objectui#12109.
Verdict
-
Shape:
- The PR is a draft on
main. Its first line isFixes #12108, andClause-②: yessits at the start of a line. - No other card number sits beside a closing keyword. The assignee is
os-zhuang. - 17 files, +415/−18. Nothing touches
content/docs/releases/. NOT GOVERNED.
- The PR is a draft on
-
Against the card's "Done when":
- "Set automatically when saved." shows in the field's description slot exactly while the field is server-owned (
isServerOwnedValue, the classifier that already lowersrequired), its value is empty, and the control takes input. That is exactly when the submit leaves the key out for the server to fill. - A literal default is seeded as before (CONTROL).
- Required-field measurement: no defect. A required date or select with a CEL default submits empty and unmarked in six routes, and objectstack's insert path resolves defaults before validating. Pinned as behaviour.
- No client-side CEL evaluation, and no new server door.
- "Set automatically when saved." shows in the field's description slot exactly while the field is server-owned (
-
Measured on
mainfirst: a CEL or runtime-token default was left unseeded with nothing on screen saying so. The ablations went red as predicted (hint removed: 17 red; the disabled gate removed: 2; the empty-value check removed: 1), and the restores are blob-equal. -
Changeset prose, checked sentence by sentence against the diff:
- the summary;
- "Where it shows" (the description slot, after any authored description, in every object-form layout);
- "When it shows" (server-owned and empty);
- "When it does not" (edit form; disabled or read-only, hence
mode: 'view'; a boolean, which starts atfalse); - "Required fields: Measured unchanged";
- the locale key and the Clause-② paragraph.
All match.
@object-ui/componentsand@object-ui/i18nareminor, and the fixed group moves together. -
Contract review: record
6105939071reads PASS.Served-tier: CONTRACT_REVIEW_TIER;Head-shais this head;Local-runs: none.Implemented-byis the dev's branch, andReviewed-byis this session. -
Gates at this reading: 37 success, 3 skipped, 2 in progress (
Testshards 1 and 7), 0 failed. Landing waits for every check green on this head. -
Out of scope, one line each:
- Filed objectui#12121: a boolean field whose
defaultValueis a CEL expression submitsfalseon create, becauseform.tsxseeds booleansfalseand the server never applies the declared default. It is class (a), measured through the realObjectFormandModalForm. Labelledfindingfor triage. apps/console/src/components/FormPage.tsx(the console's second form renderer) omits server-owned blanks but draws no hint: noted, not filed (read, not measured; carrier none).- The containers pass
previousValuesin edit mode only, so the renderer reads amode: 'view'form as create: noted, not filed (no observable wrong answer today; the hint is gated off disabled controls). - The dev left a stray
/tc.pidin its container, outside every repo: nothing lands.
- Filed objectui#12121: a boolean field whose
Landing: once every check on this head is green, the seat flips the PR to ready and arms auto-merge, which enters the merge queue. The merge closes this card.
Generated by Claude Code
- File surface, extended: the hint lands in
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsLanded: PR objectui#12120 →
mainb7cd5c89·domain:uiseat 1,session_01AswpQDLCKiZos2jCXknwKz, 2026-10-11T06:07Z- The merge: through the merge queue. Its one parent is
206505c3, and it is an ancestor oforigin/main. - Content check: all 17 files the merge changes are blob-identical to the reviewed head
662a0ba440. ACCEPT6105946569and contract review6105939071(PASS) cover that head. - The card: closed
completedby the PR'sFixesline.pm:dispatchedis removed in this act. The opendomain:uiset lost this card and no other. - Carried: the boolean-default defect this work measured is objectui#12121, labelled
findingfor triage.
Generated by Claude Code
- The merge: through the merge queue. Its one parent is
Filing gate ①: a product defect measured at a public door (findings A-20, B-11; the CEL half). Filed by the triage seat (seat post objectstack-ai/objectstack#6015,
session_01AavokzJ5DndAwitDXvKy4U), splitting objectstack-ai/objectstack#22722 (the maintainer's HotCRM browser pass, objectstack-ai/hotcrm#2058, measured on@objectstack/*17.7.0). ⛔ Not a claim.Read on objectui
maince991bd70fpackages/plugin-form/src/schemaDefaults.ts(schemaDefaultValues,isSeedableDefault, and the docblock at about:56–:73) deliberately leaves a CEL or runtime-token default unseeded. The client cannot evaluate it, and the server resolves it at insert.default: truehalf is fixed onmainby objectui PR fix(plugin-form): a create form preselects the option a field marks default: true (objectui#11914) #11944 ([finding] console(create form): a required select field whose options declare default: true renders "Select an option" instead of preselecting the declared default, so the newcomer tutorial's Create Ticket form blocks on Priority and Status #11914, closed), after the 17.7.0 pin0abd4f9f. It ships with the next console release.Done when (the cheapest truthful fix; ⛔ no client-side CEL evaluation, ⛔ no new server door in this card)
domain:ui· p3.