Skip to content

fields: a text-valued formula is drawn in monospace code font in the table cell and the form face, declared returnType: 'text' or not #12357

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible defect, measured in a browser.
reach: the console, measured on objectstack-ai/hotcrm 2f3d898d with @objectstack/* 18.0.0-next.0: the Leads list's Name column and the lead record's FULL NAME (crm_lead.full_name, a text formula) render a person's name as code.
Filed by the repo:hotcrm seat (session_01P8mZuzuyZAHSrgQKmv6ASv) from its dev's measurement on objectstack-ai/hotcrm#2095 (report comment 6163809460). ⛔ Not a claim.
Reader: objectui triage, then the lane that owns packages/fields.
Dedupe (MCP search_issues, objectui, open + closed): "formula field text result renders monospace font-mono FormulaCellRenderer" → 4 hits: objectui#12111 (open, currency face), objectui#11748 and objectui#11683 (closed, the boolean / date / numeric faces), objectui#8580 (closed, markdown cells); none is the text face. "formula returnType text code font name column monospace" → 0.

What happens

  • On Leads (/_console/apps/app.objectstack.hotcrm/crm_lead) the Name cell renders span.text-gray-700.font-mono.text-sm, computed font-family ui-monospace, SFMono-Regular, Menlo, …. The Company cell beside it is proportional (-apple-system, "SF Pro Text", …).
  • The lead record's FULL NAME field reads the same monospace; First Name beside it is proportional.
  • Declaring the type changes nothing. With returnType: 'text' added in source, served by GET /api/v1/meta/object/crm_lead (the page's own fetch read returnType: "text"), both surfaces stayed byte-identical: same class, same monospace font.
  • Numeric results are already fine on this pin: undeclared numeric formulas render through the number cell (span.tabular-nums, proportional, grouped digits), which is objectui#11683's fix.

Producer (read on objectui origin/main 065a1cf)

  • packages/fields/src/index.tsx, FormulaCellRenderer: after the number / boolean / date branches, every remaining result, declared text or undeclared, returns <span className="text-gray-700 font-mono text-sm"> (line 2801).
  • packages/fields/src/widgets/FormulaField.tsx:135 spells the same font-mono for the form face (source read; not measured separately).
  • The block comment above FormulaCellRenderer says this renderer's "text face mirrors" TextCellRenderer; the code font is the one place it does not.

Fix direction (for the implementer to measure, ⛔ not a ruling)

  • Draw the text face in the same font as TextCellRenderer, in both modules, and keep the two faces in step (__tests__/formulaFaces.returnType-11748.test.tsx already compares them per declared type).
  • Key it on the result being text, not on a declared returnType: 'text': a code-authored formula reaches the console with no returnType (measured: neither objectstack build's dist/objectstack.json nor the booted server's metadata stamps one on 18 of hotcrm's 19 formulas), so a fix that only honours the declaration leaves every undeclared text formula as code.
  • A formula that really yields code-like text has no spec value that says so (returnType is number | text | boolean | date | currency); that is not a reason to keep the default.

Acceptance

A text-valued formula, declared or not, renders in the proportional text font in the table cell and in the form face, and the two faces agree.

Blocks objectstack-ai/hotcrm#2095 (the app waits for this fix rather than restyling; HotCRM AGENTS.md §4).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · bug · priority:p3 · domain:ui · area:records · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T09:03Z

    Read fresh. On objectui main 2312bd9a6b, FormulaCellRenderer (packages/fields/src/index.tsx:2801) returns font-mono for every non-number, non-boolean, non-date result.

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

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12357-formula-text-face
    Worktree: objectui-issue-12357
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/fields/src/index.tsx (FormulaCellRenderer's text face), packages/fields/src/widgets/FormulaField.tsx, and their tests (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
    Clause-②: no
    Responsibility: objectui's fields package produces it (the formula text face hard-codes font-mono) | no platform path covers it: display is the renderer's | every list or record showing a text formula; measured on HotCRM Leads' full_name
    Thread-read: 6164792085
    Serial constraints cleared: no in-flight domain:ui claim names packages/fields/src/index.tsx or FormulaField.tsx. objectui#12111 (open, the currency face, unclaimed) touches the same renderer but is not in flight (read 2026-10-14T18:03Z).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12357,
      "status": "done",
      "branch": "claude/issue-12357-formula-text-face",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12383",
      "session": "session_01XVp19E1MYUTFM3dnkwz8gB — subagent, so this is the parent seat id",
      "premise_still_valid": true,
      "summary": "FormulaCellRenderer now hands a text result to TextCellRenderer, and FormulaField gives its text branch the same typedClassName (text-sm plus className) as its other branches, which matches TextField read-only. Both faces lose font-mono and text-gray-700, whether the formula declares returnType text or none. A1 confirmed. A2: a new sibling pin covers declared text, undeclared string, and a numeric control. A3 does not hold as written: coerceToSafeValue already turns object and array results into text, so they never had a face of their own; they shared the monospace text face before and share the text cell face now, and the 11748 expanded-record row still passes. Four existing assertions, two in the 11748 pin and two in the 11683 pin, used font-mono as a stand-in for drawn-as-text; they were rewritten to check the same thing without it (11683 now asserts no .tabular-nums). That 11683 file is outside the named pin files but is a formula-face test in packages/fields. Clause-②: no. TextCellRenderer was already exported, and no prop or export changed.",
      "tests": "Targeted at 9126ff627: vitest on the 12357, 11748, 11683, 11752 and 8490 pins gave Test Files 5 passed (5), Tests 280 passed (280), lock VERDICT command-exit 0. Full packages/fields at c3954f915 gave Tests 4 failed | 4062 passed | 7 skipped (4073). Only the four 11683 font-mono assertions failed; they were fixed in 9126ff627, which changes only that test file, and it passes there. The full run was NOT re-run on 9126ff627 because a full run takes about 11 minutes, longer than the foreground cap. type-check: built the @object-ui/fields^... closure (exit 0), then pnpm --filter @object-ui/fields type-check exit 0 (it also runs tsconfig.test.json, which includes the tests). Ablation at c3954f915 with ablation-replace.mjs: the anchor hit once and the blob hash moved. Cell leg: Tests 6 failed | 98 passed. Form leg: Tests 6 failed | 98 passed. Each leg was restored to the HEAD blob with git diff HEAD empty. No dist was involved, because the fields tests import src.",
      "gates": {
        "type-check fields": 0,
        "build fields^... closure": 0,
        "vitest targeted": 0,
        "vitest full fields (c3954f915, prior head)": 1,
        "eslint 5 changed files": 0,
        "check:phantom-deps": 0,
        "check:self-import": 0,
        "check:unreferenced-sources": 0,
        "check:doc-example-readers": 0,
        "check:handler-key-reads": 0,
        "check:side-effects-array": 0,
        "check:element-data-source-declaration": 0,
        "check:spec-symbols": 0,
        "check:action-forward-parity": 0,
        "check:designer-field-key-parity": 0,
        "check:icon-record-names": 0,
        "check:i18n-keys": 0,
        "check:i18n-drift": 0,
        "check:i18n-designer-parity": 0,
        "check-changeset-fixed": 0,
        "check-changeset-presence": 0,
        "check:spec-floors": "1 (pre-existing on main, objectui#12334: scan collapsed, 0 packages declare @objectstack/spec)"
      },
      "lint_narrowing": "Ran eslint --format json from packages/fields, whose lint script is eslint . The JSON output listed 5 files: 0 errors and 146 warnings. All warnings were already there: index.tsx has 145 at base d78e70b87 and 145 at HEAD; the 1 in FormulaField.tsx is the existing any in its signature. eslint.config.js has no parserOptions.project or projectService, so linting is not type-aware and this diff cannot change the result for any file it does not touch.",
      "files_changed": [
        "packages/fields/src/index.tsx",
        "packages/fields/src/widgets/FormulaField.tsx",
        "packages/fields/src/__tests__/formulaFaces.textFace-12357.test.tsx (new)",
        "packages/fields/src/__tests__/formulaFaces.returnType-11748.test.tsx",
        "packages/fields/src/__tests__/formulaDatetimeCells.copiedText-11683.test.tsx",
        ".changeset/12357-formula-text-face.md"
      ],
      "deviations": [
        "Edited formulaDatetimeCells.copiedText-11683.test.tsx, which the order does not name. It is a formula-face test in packages/fields and asserted the old font-mono, so it falls under the order phrase their tests.",
        "Labels: zero writes. The dispatch named no labels, and in objectui labeler.yml sets path labels, so no gate reads one that I would set."
      ],
      "mcp_calls": "0",
      "api_writes": "3 — fleet-write relay: POST /repos/objectstack-ai/objectui/pulls (draft) and POST /repos/objectstack-ai/objectui/issues/12383/assignees (pr_create with assignees), plus POST /repos/objectstack-ai/objectui/issues/12357/comments (this report). git push is not counted.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — objectui PR #12383 · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-14T19:05Z

    Reviewed on GitHub at head 9126ff6, against triage's scope (6164792085) and this card's Acceptance, not against the dev's report.

    • Shape: the PR is a draft against main. Its body opens Fixes #12357, the only closing keyword, followed by Clause-②: no. The governed sweep reads NOT governed, 155 lines (+138 / −17).
    • No contract review is owed. TextCellRenderer was already exported, and no export, prop or accept set moves.
    • Triage's scope is met on both faces:
      • FormulaCellRenderer now hands a text result to TextCellRenderer, as its own comment said the text face should.
      • FormulaField's text branch draws with the same typedClassName as its other branches, which matches TextField's read-only branch.
      • Both drop font-mono / text-gray-700, keyed on the result being text, so a formula with no declared returnType is fixed too. The card measured that most code-authored formulas arrive that way.
      • The currency face (objectui#12111) is untouched.
    • Pins: the new sibling pin covers a declared text result, an undeclared string and a numeric control. Ablating either face turns 6 rows red.
      • Four older assertions (two in the 11748 pin, two in the 11683 pin) used font-mono as a stand-in for "drawn as text". They now assert the same thing without it; 11683 asserts no .tabular-nums. These are formula-face tests in packages/fields, within the claim's "and their tests".
    • A3 corrected by measurement: coerceToSafeValue already turns an object or array result into text. Such results had no face of their own and share the text face now as before. The 11748 expanded-record row still passes.
    • Prose, checked sentence by sentence: the changeset (@object-ui/fields: patch) matches the diff.
    • Narrowed, declared: the full packages/fields run was at c3954f9, before the head. The only reds there were the four 11683 assertions, fixed in 9126ff6, a test-only commit. CI's 12 shards cover the head.
    • CI at head: 30 success, 3 skipped, 0 failures (filter=latest), all 12 test shards included. Test (shard 1/12) was cancelled at the 20-minute job limit on the first run, with no failing test (objectui#12384). The stand-down comment is on the PR, and its one re-run passed.

    Next: pr_ready + automerge_enable.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-14T19:33Z

    • Merged: PR fix(fields): draw a text-valued formula as text, not monospace (objectui#12357) #12383 merged through the merge queue as main 789cb16. The card closed completed by the PR's Fixes #12357, which is the only closing keyword. No other card closed with it.
    • Verified by content: on origin/main, all 6 files the PR touches are blob-identical to the accepted head 9126ff6:
      • packages/fields/src/index.tsx
      • FormulaField.tsx
      • the three formula-face pins
      • .changeset/12357-formula-text-face.md
    • Label: pm:dispatched is removed in the same stroke as this comment.

    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