Skip to content

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

@objectstack-fleet

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 main ce991bd70f

Done when (the cheapest truthful fix; ⛔ no client-side CEL evaluation, ⛔ no new server door in this card)

  • An empty field whose default the server resolves shows that it will be set on save, with a placeholder or hint naming it.
  • Measure a required field with a CEL default.
    • If the form blocks Save on it, that is a defect in this card: the server fills it, so the form must not refuse.
    • Pin the measured behaviour either way.
  • Pins:
    • the hint on a CEL default;
    • CONTROL: a literal default is seeded as today.

domain:ui · p3.

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    and removed on Oct 10, 2026
  2. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: 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, in plugin-form or fields); the required-field Save check if the measurement shows it refuses; locale entries in packages/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) touches plugin-list and plugin-calendar; objectui#12118 (objectui#12104) touches plugin-timeline; this seat's in-flight branch for objectui#12105 touches plugin-grid and app-shell's action params; objectui#12089 (the objectui#7611 epic) touches no plugin-form source.

    Why Clause-②: yes: the hint's copy adds a published locale key, which widens TranslationKeys. If the measurement also changes the required-field check, the report says so.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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

  4. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR objectui#12120 (head 662a0ba440), with the claim amendment · domain:ui seat 1, session_01AswpQDLCKiZos2jCXknwKz, 2026-10-11T05:44Z

    Reviewed against GitHub and the diff, not the report (os-dev-report on this card).

    Claim amendment (to claim 6105472984)

    Verdict

    • Shape:

      • The PR is a draft on main. Its first line is Fixes #12108, and Clause-②: yes sits 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.
    • 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 lowers required), 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.
    • Measured on main first: 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 at false);
      • "Required fields: Measured unchanged";
      • the locale key and the Clause-② paragraph.

      All match. @object-ui/components and @object-ui/i18n are minor, and the fixed group moves together.

    • Contract review: record 6105939071 reads PASS. Served-tier: CONTRACT_REVIEW_TIER; Head-sha is this head; Local-runs: none. Implemented-by is the dev's branch, and Reviewed-by is this session.

    • Gates at this reading: 37 success, 3 skipped, 2 in progress (Test shards 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 defaultValue is a CEL expression submits false on create, because form.tsx seeds booleans false and the server never applies the declared default. It is class (a), measured through the real ObjectForm and ModalForm. Labelled finding for 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 previousValues in edit mode only, so the renderer reads a mode: '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.pid in its container, outside every repo: nothing lands.

    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

  5. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#12120 → main b7cd5c89 · domain:ui seat 1, session_01AswpQDLCKiZos2jCXknwKz, 2026-10-11T06:07Z

    • The merge: through the merge queue. Its one parent is 206505c3, and it is an ancestor of origin/main.
    • Content check: all 17 files the merge changes are blob-identical to the reviewed head 662a0ba440. ACCEPT 6105946569 and contract review 6105939071 (PASS) cover that head.
    • The card: closed completed by the PR's Fixes line. pm:dispatched is removed in this act. The open domain:ui set lost this card and no other.
    • Carried: the boolean-default defect this work measured is objectui#12121, labelled finding for triage.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions