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 }}
This repository was archived by the owner on Oct 26, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Oct 26, 2026. It is now read-only.
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
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-familyui-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/main065a1cf)
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).
Read fresh. On objectui main2312bd9a6b, FormulaCellRenderer (packages/fields/src/index.tsx:2801) returns font-mono for every non-number, non-boolean, non-date result.
priority:p3: display only. A text formula (a person's name) reads in a code font in the cell and the form. Nothing is lost.
Scope: the text face mirrors TextCellRenderer, as the renderer's own comment says, on both faces (the cell, and FormulaField.tsx:135), with a pin for a declared returnType: 'text' and an undeclared one.
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).
{
"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": []
}
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.
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
bug·priority:p3·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T09:03ZRead fresh. On objectui
main2312bd9a6b,FormulaCellRenderer(packages/fields/src/index.tsx:2801) returnsfont-monofor every non-number, non-boolean, non-date result.priority:p3: display only. A text formula (a person's name) reads in a code font in the cell and the form. Nothing is lost.full_name, …) declare noreturnType, and the console draws an untyped formula as code hotcrm#2095 (p3) waits on it.TextCellRenderer, as the renderer's own comment says, on both faces (the cell, andFormulaField.tsx:135), with a pin for a declaredreturnType: 'text'and an undeclared one.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12357-formula-text-faceWorktree:
objectui-issue-12357Domain:
domain:uiSeat:
domain:ui#2File 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/objectuireads 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_nameThread-read: 6164792085
Serial constraints cleared: no in-flight
domain:uiclaim namespackages/fields/src/index.tsxorFormulaField.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
objectstack-fleet commentedon Oct 14, 2026
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
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT — objectui PR #12383 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T19:05ZReviewed on GitHub at head
9126ff6, against triage's scope (6164792085) and this card's Acceptance, not against the dev's report.main. Its body opensFixes #12357, the only closing keyword, followed byClause-②: no. The governed sweep reads NOT governed, 155 lines (+138 / −17).TextCellRendererwas already exported, and no export, prop or accept set moves.FormulaCellRenderernow hands a text result toTextCellRenderer, as its own comment said the text face should.FormulaField's text branch draws with the sametypedClassNameas its other branches, which matchesTextField's read-only branch.font-mono/text-gray-700, keyed on the result being text, so a formula with no declaredreturnTypeis fixed too. The card measured that most code-authored formulas arrive that way.textresult, an undeclared string and a numeric control. Ablating either face turns 6 rows red.font-monoas 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 inpackages/fields, within the claim's "and their tests".coerceToSafeValuealready 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.@object-ui/fields: patch) matches the diff.packages/fieldsrun was atc3954f9, before the head. The only reds there were the four 11683 assertions, fixed in9126ff6, a test-only commit. CI's 12 shards cover the head.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
objectstack-fleet commentedon Oct 14, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T19:33Zmain789cb16. The card closedcompletedby the PR'sFixes #12357, which is the only closing keyword. No other card closed with it.origin/main, all 6 files the PR touches are blob-identical to the accepted head9126ff6:packages/fields/src/index.tsxFormulaField.tsx.changeset/12357-formula-text-face.mdpm:dispatchedis removed in the same stroke as this comment.Generated by Claude Code
fix(fields): draw a text-valued formula as text, not monospace (objec…