Skip to content

console rendering, from the HotCRM pass: column label precedence, star rating widget, reference rail (nameField, refresh), gantt year, approval drawer approver and headline, public form heading and editor hints #12110

Description

@objectstack-fleet

Filing gate ①: seven small rendering defects measured at a public door (findings A-02, A-03, B-13, B-14, B-20, C-18, A-09, B-08). 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.

One card on purpose: each item is a few lines in one file, all are domain:ui, and none needs a decision. One PR, with a pin per item.

Items (read on objectui main ce991bd70f)

  1. A view's explicit column label loses to the field label (A-02).
    • packages/plugin-grid/src/ObjectGrid.tsx about :3508–:3511 passes col.label as the fallback of a translated field-label lookup.
    • An authored column label must win over the field's label.
  2. type: rating with widget: 'star_rating' renders as a text box (A-03).
    • packages/components/src/renderers/form/form.tsx resolves widget first (about :3131), finds no field:star_rating, and falls to the default text input (about :4480).
    • That contradicts the spec's promise that an unregistered widget "degrades to the type renderer".
    • packages/fields/src/index.tsx (about :3769) maps rating cells to a number.
    • Fall back to the type renderer, and give rating a rating cell.
  3. The reference rail ignores nameField (B-13). packages/plugin-detail/src/renderers/record-reference-rail.tsx pickDisplayName (about :226–:245) uses a fixed ladder, and the fetch (about :440–:460) keeps only .fields of the target schema.
  4. refreshAfter: true does not refresh the reference rail (B-14).
    • The same file latches its fetch on parentId::entriesSig (about :304, :370–:377) and does not subscribe to the data-invalidation bus.
    • RecordDetailView.tsx notifyRecordChanged (about :557, :1116) posts only there.
  5. Gantt dates show no year (B-20). packages/plugin-gantt/src/GanttView.tsx formats { month, day } at about :3978, :4000, :4013, :4302, :4522 and :4837. Show the year at least when it is not the current one.
  6. Approval drawer (C-18), in apps/console/src/pages/system/ApprovalsInboxPage.tsx:
  7. Public form heading and editor hints (A-09, B-08):
    • apps/console/src/components/FormPage.tsx loadPublicForm (about :1436) falls back to the object's API name. Use the object's label.
    • packages/fields/src/widgets/RichTextField.tsx about :417–:418 shows "Format: markdown" and "Rich text editor (basic)" to anonymous visitors. Hide editor hints on the public surface.

Not in this card: a completed task's due date shown as "Overdue" (C-19) is #11815, on hold until objectui installs a spec release with settledWhen.

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 (all seven items, one PR, a pin per item, as the card asks)
    Session: session_01TYgwmFK1q4KJ6Qq2WRLzsD
    Account: zhuangjianguo
    Branch: claude/issue-12110-hotcrm-rendering
    Worktree: objectui-issue-12110
    Domain: domain:ui
    Seat: domain:ui#2
    File surface:

    • item 1: packages/plugin-grid/src/ObjectGrid.tsx (the column label precedence);
    • item 2: packages/components/src/renderers/form/form.tsx (the unregistered-widget fallback) and packages/fields/src/index.tsx (a rating cell);
    • items 3 and 4: packages/plugin-detail/src/renderers/record-reference-rail.tsx (pickDisplayName, the fetch, and the invalidation subscription). RecordDetailView.tsx is read only, unless the bus measurably needs a post from there;
    • item 5: packages/plugin-gantt/src/GanttView.tsx (the date formats);
    • item 6: apps/console/src/pages/system/ApprovalsInboxPage.tsx (formatIdentity and decisionAmountEntry only);
    • item 7: apps/console/src/components/FormPage.tsx (loadPublicForm's heading) and packages/fields/src/widgets/RichTextField.tsx (the editor hints on the public surface);
    • their tests, the docs pages the changes describe, and .changeset/12110-*.md.

    (stop on breach; explain in the report)
    Container & model: M (seven small items, one PR), mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"
    Clause-②: no
    Responsibility: objectui renderers: grid column label, form widget fallback, reference rail, gantt date format, approval drawer, public form heading and editor hints, each rendering a wrong or incomplete face of metadata it already receives | none needed: each item is a renderer reading what the spec already declares (column label, the type renderer fallback, nameField, the data-invalidation bus, the object label) | every HotCRM-style app; measured in the maintainer's HotCRM browser pass (objectstack-ai/hotcrm#2058, 17.7.0)
    Thread-read: none
    Serial constraints cleared: none blocking. Read at 2026-10-11T10:54Z:

    Clause-②: no is a prediction. If an item adds a locale key to @object-ui/i18n's packs or a new export, the published surface widens; the report says so, and the seat corrects the line and runs the review at CONTRACT_REVIEW_TIER.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12110,
      "status": "needs_decision",
      "branch": "claude/issue-12110-hotcrm-rendering",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12151",
      "session": "session_01TYgwmFK1q4KJ6Qq2WRLzsD — the seat's id (mode:subagent: the parent's)",
      "premise_still_valid": true,
      "summary": "Draft PR #12151 (head fd4059c, merged with main 158c75d) lands items 1 to 6 and the heading half of item 7, one commit and one pin per item, each pin red on BASE c9fc10b1ec first. Item 1: an authored column label is the header as written. Item 2: a widget hint is taken only when it can render (registered field widget, built-in control, native email/password input or secret refusal), else the type renderer, per the spec text of FieldSchema.widget (checked in installed spec 17.7.0 and on objectstack main b7cd1af9df; star_rating is not a spec-declared widget, so fallback, not registration); rating cells draw stars (module-private renderer, no new export). Items 3 and 4: the reference rail names rows by the target nameField and refetches in place on the invalidation bus (parent record, entry objects, star); RecordDetailView not touched. Item 5: six gantt faces add the year when not the current one. Item 6: position approvers read the registry label via useMetadata().ensureType('position') (the store #12089 / #7611 already uses), and the headline prefers a declared currency field and never headlines a name-matched zero. Item 7 heading: object label before API name. STOPPED: item 7 second half (editor hints on the public surface) needs a contract decision; the PR first line is Part of #12110 and names it. Clause-2 stays no (no locale key, no new export).",
      "tests": "Post-merge at fd4059c: turbo type-check of plugin-grid, components, fields, plugin-form, plugin-detail, plugin-gantt green (41 of 42 tasks; console cut by the cap behind the dep rebuild), then console alone 36/36 green; pins + main neighbours (pagerButtonNames-12124, affordanceGrantMap-12082) + column-identity ratchet: 9 files, 82 tests passed; packages/plugin-detail/ 4 shards: 255 passed, 1 skipped file; packages/components/ 6 shards: 375 passed, 1 skipped file; scripts/__tests__/ 3 shards: 180 passed, 2 skipped files. Pre-merge at 50c7747: plugin-grid 202 passed; plugin-gantt 99 passed, 1 skipped file; fields 245 passed, 1 skipped file; plugin-form 175 passed; apps/console 181 passed. Red on base (one run, base source): item 1 headers [#, Account Name, Industry] and [#, 客户名称, 行业]; item 2 no rating probe, ObjectForm zero stars, cell zero stars; item 3 CS-0001 not found; item 5 3/2 where 3/2/25 belongs; item 6 chip positi…ager, lead child_account_revenue; item 7 heading ats_inquiry. Item 4 ablation (fix committed, base rail blob written over it, restored with git checkout HEAD, blob hash equal to HEAD, diff HEAD empty): both item 4 legs and the item 3 leg red, 2 controls green. Item 7b measured on base: the anonymous /f/SLUG page shows Format: markdown (throwaway test, deleted, never committed). Eager closure: NOT MEASURED as the console eager closure (needs two console builds); proxy dist/index.js base vs head: fields +776 B raw / +170 B gzip, components +218 B raw / +79 B gzip, both builds proven landed (marker count 1 to 2, sha changed).",
      "mcp_calls": "1 — mcp__claude-code-remote__add_repo (objectstack-ai/hotcrm, access read; answered read_available, nothing attached; used for one shallow read-only clone into the scratchpad, since deleted). No GitHub MCP tool called.",
      "api_writes": "2 relay strokes (fleet-write, landing as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/12151/assignees [zhuangjianguo], read back 13366/13366 bytes identical; (2) this os-dev-report comment = POST /repos/objectstack-ai/objectui/issues/12110/comments. Plus git push (not REST). Zero label writes, no PR-body PATCH.",
      "open_questions": [
        {
          "question": "Item 7, second half: hide the rich-text editor hints (Format: markdown, Rich text editor (basic)) on the anonymous public form. RichTextField cannot tell it renders there: FormPage knows its mode, but FieldWidgetComponentProps has no such member and no context carries it. Which route?",
          "options": [
            "A — a new optional member on the widget contract (FieldWidgetComponentProps), produced only by FormPage in public mode, read by RichTextField. About 40 lines + pin; @object-ui/fields published type widens (Clause-2 yes, minor). Business need: measured (HotCRM A-09/B-08, reproduced on main). Long-term: grows a widget contract for one producer and one reader, and sets a precedent for per-surface chrome flags. AI-safety: every widget, including AI-authored ones, may now receive a key it must keep off its DOM (toDomProps whitelist). Startup focus: a new declared surface with one consumer.",
            "B — drop both hint lines from RichTextField's editable header on every surface (the field's own help text and placeholder remain where authored guidance lives). About 10 lines + pin; no contract change, no export. Business need: the basic-editor line describes the implementation and informs no user anywhere; the format label is the only loss, for internal authors, with no measured demand for it. Long-term: removes chrome rather than adding a switch. AI-safety: nothing new to learn or misuse. Startup focus: shrinks the surface. Cost: internal console forms lose the two lines too, a change the card did not ask for.",
            "C — a scoped React context (for example exported from @object-ui/fields or @object-ui/react) that FormPage public mode provides and RichTextField reads. About 40 lines; a new public export (Clause-2 yes); a context nobody else reads; the dispatch's no-global-flag line argues against it.",
            "D — leave the hints and close this half with the reading. Zero cost; anonymous visitors keep seeing editor chrome."
          ],
          "recommendation": "B, because on all four axes it is the only option that fixes the measured defect without widening a contract: the hints carry no information a visitor or most users need, it adds no prop or export an AI-authored widget could mishandle, and it shrinks rather than grows the surface. If the maintainer wants the syntax shown to internal authors, D for the public half is the fallback, not A or C."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — ObjectForm's numeric formField.step now reaches the DOM only through a widget: input hint (or a host with no field widgets registered); a dormant producer after item 2. In the PR's Acceptance notes.",
        "carrier: none · noted, not filed — the gantt shift-mode header labels each shift-day with month and day only and shift mode has no year tier; same family as item 5, not measured at a public door. In Acceptance notes.",
        "carrier: whoever next touches packages/app-shell/src/utils/approverIdentity.ts · noted, not filed — the record page approval panel names a position from sys_position rows (useApproverDirectory, the spec APPROVER_VALUE_BINDINGS source) while the inbox now reads the position registry; a registry-only position reads differently on the two. Unexercised drift. In Acceptance notes."
      ],
      "gates": "All at fd4059c unless noted. type-check: 6 lib packages green + console 36/36 green. vitest: pins + neighbours + column-identity ratchet 9 files / 82 tests; plugin-detail 255+1 skipped; components 375+1 skipped; scripts/__tests__ 180+2 skipped (includes one-authority-per-exported-name-6273); pre-merge at 50c7747: plugin-grid 202, plugin-gantt 99+1 skipped, fields 245+1 skipped, plugin-form 175, apps/console 181. node scripts/check-changeset-presence.mjs exit 0 (23 source files of 7 released packages, 1 changeset); check:changeset-claims 0; check:pending-changeset-literals 0; check:new-line-citations 0 (0 new citations); check:control-bytes 0; check:test-path-roots 0; check:vi-mock-specifiers 0; check:vi-mock-inherit 0; check:vi-mock-override-shape 0; check:phantom-deps 0; check:doc-json 0; check:doc-fences 0. i18n family: not run, no locale key changed. eslint: 22 changed TS files, 0 errors (narrowed, see deviations). CI: in_progress at report time (not awaited).",
      "line_budget": "Against merge-base 158c75d: 28 files, +1289 / -90 (1379 changed lines, tests included); non-test files 13, +380 / -51. No line budget was declared by the claim or the dispatch.",
      "deviations": [
        "One console tsc --noEmit ran outside the verify lock (to read a type error quickly); re-run at once under the lock through turbo type-check, green.",
        "File surface exceeded, each forced by an item: plugin-form ObjectForm.tsx (a comment item 2 made false) and two plugin-form tests that relied on the retired unregistered-widget route (re-pointed at widget: input); ApprovalsInboxPage.tsx beyond formatIdentity / decisionAmountEntry (usePositionLabels hook, label map threaded through approverDisplay / approverChips); form.tsx cascade-clear reader; five existing pins re-pointed by item 5 (displayLocale-10668, inclusiveEnd-11141, catalogPlans-11141, objectGanttInputs-11168 clock pin) and one by item 6 (summaryFaces-11677 lead-label leg on a billed invoice); FormPage.publicHeading pin moved one arm down by item 7. Docs (pre-authorised): plugin-grid.mdx, plugin-form.mdx, plugin-gantt.mdx, guide/public-forms.md.",
        "eslint was run on the 22 changed TS files only, not as the package-level eslint . form; the config enables no type-aware linting, so unchanged files cannot change verdict. Four new no-explicit-any warnings sit in two new test files.",
        "Eager-closure delta reported as a package-dist proxy, not the console eager closure.",
        "One verify-lock attempt ended queue-timeout (99, never acquired) and was re-entered on the same slot; one invocation was refused by the vitest guard (overlapping filters) and one (a mistyped path) — both re-run correctly; none counted as a measurement.",
        "Interrupted by the API usage wall around 14:15Z; resumed after the worker restart from the pushed head 50c7747, merged main 158c75d, re-ran what the merge touches.",
        "Cleanup done: worktree /home/user/objectui-issue-12110 removed (no --force), its node_modules deleted, the scratch hotcrm clone deleted; no background process of this run remains."
      ],
      "files_changed": [
        ".changeset/12110-hotcrm-rendering.md",
        "apps/console/src/components/FormPage.tsx",
        "apps/console/src/components/FormPage.publicHeading.test.tsx",
        "apps/console/src/pages/system/ApprovalsInboxPage.tsx",
        "apps/console/src/pages/system/ApprovalsInboxPage.approverHeadline-12110.test.tsx",
        "apps/console/src/pages/system/ApprovalsInboxPage.summaryFaces-11677.test.tsx",
        "content/docs/guide/public-forms.md",
        "content/docs/plugins/plugin-form.mdx",
        "content/docs/plugins/plugin-gantt.mdx",
        "content/docs/plugins/plugin-grid.mdx",
        "packages/components/src/renderers/form/form.tsx",
        "packages/components/src/renderers/form/__tests__/form-unregistered-widget-type-fallback-12110.test.tsx",
        "packages/fields/src/index.tsx",
        "packages/fields/src/__tests__/cellRenderers.ratingCell-12110.test.tsx",
        "packages/plugin-detail/src/renderers/record-reference-rail.tsx",
        "packages/plugin-detail/src/renderers/__tests__/record-reference-rail.nameFieldRefresh-12110.test.tsx",
        "packages/plugin-form/src/ObjectForm.tsx",
        "packages/plugin-form/src/__tests__/objectFormNumericStep-9574.test.tsx",
        "packages/plugin-form/src/__tests__/objectFormUnregisteredWidget-12110.test.tsx",
        "packages/plugin-form/src/__tests__/sectionEntryOverrides-10475.test.tsx",
        "packages/plugin-gantt/src/GanttView.tsx",
        "packages/plugin-gantt/src/GanttView.displayLocale-10668.test.tsx",
        "packages/plugin-gantt/src/__tests__/GanttView.dateYear-12110.test.tsx",
        "packages/plugin-gantt/src/__tests__/GanttView.inclusiveEnd-11141.test.tsx",
        "packages/plugin-gantt/src/__tests__/ObjectGantt.catalogPlans-11141.test.tsx",
        "packages/plugin-gantt/src/__tests__/objectGanttInputs-11168.test.tsx",
        "packages/plugin-grid/src/ObjectGrid.tsx",
        "packages/plugin-grid/src/__tests__/ObjectGrid.authoredColumnLabel-12110.test.tsx"
      ]
    }

    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 seatpm:dispatchedpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions