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 }}
Repository navigation
Lay out forms and record pages for the work done on them: required contact email, lead form, email dialog, product price prefill, highlights, case type and SLA colour, article body and votes, call and meeting prefill, related tabs, list record titles #2369
Ruled: Irene · 2026-10-29T02:08Z · the UX plan of the #2320 browser pass: six HotCRM cards plus objectstack suggestion cards, queued (4+5) → 6 → 10 → 9 → 7 → (8+3). This is card 5 of 6, themes 7 (forms and record pages), with the record titles of theme 2.
Source
The #2320 browser pass on platform 18.2.0 (six tracks, 115 UI suggestions). Filing gate ③ a task the maintainer directed. Each row cites its track report on #2320 and the suggestion id there; the steps and screenshots are in that track's REPORT.md (project files qa-2320/<track>/). The platform parts of the same themes are objectstack#27355 (console) and objectstack#27356 (Setup and Studio).
Measure each row first on origin/main. A row that is already fixed, or that turns out to need the platform, is not forced: report it with the evidence and move on.
#
Observed on 18.2.0
Report
Target
1
New Contact: the required Email is on the third tab; Create jumps there with an error.
Prefilled on pick, if 18.2.0 forms can; otherwise a platform finding. #2318 (price books) will change where the price comes from, so prefill from the field #2318 keeps.
5
The highlights strip repeats the title's name and number, while tier, health and revenue sit lower.
List cells and pickers show "ACC-000002 - Globex I…": the record title is the number plus the name (display_title, #2230), so the name is cut and the picker subtitle repeats it. Same on cases, articles, campaigns, quotes and products.
The name leads. Measure the options: a title of the name alone with the number as a column and picker subtitle, or the name first. Keep what #2230 and objectstack#24981 rely on: import matching a lookup cell by the title, search, and sort. Report the trade-off before changing the title of any object.
Done when
Each row is fixed, or reported as already fixed or platform-only, with the measurement.
A behaviour change (hook, flow, action visibility, refusal) has a test that goes red with it reverted. A metadata-only change (layout, order, label) is checked by the existing metadata and docs guards.
The in-app manual pages that describe a changed surface say what it does now, in en and zh.
pnpm verify is green, and the PR includes a changeset.
Grade: priority:p3. Daily friction on the forms people fill most. Size: M. The implementer may split the card into two PRs along the table's sections.
Filed by the repo:hotcrm lane seat (session session_01J8VVAnPkgK6CWrJa6t7Fk5) from Irene's ruling on the #2320 UX summary. Dedupe: read the 20 open hotcrm issues. None covers these rows; overlaps are named in the rows.
This order takes the slot freed by #2367 (PR #2377). It is UX card 5 in Irene's queue order: #2371 → #2366 → #2367 → #2368 → #2369 → #2370. The card body is the spec. #2368 holds the other slot.
Container & model: M · mode:thread · model TIER_DEFAULT · effort high (the thread runs at the project default). Role file:objectstack-ai/objectstack.claude/agents/os-dev.md. Read it in full. hotcrm AGENTS.md binds. Re-measure every row on origin/main (52083a5 or later). Writes:
The PR goes only through the relay op pr_create: draft, base main, assignees: [huangyiirene].
Comments go only through scripts/pm/post-stamped.mjs.
⛔ No GitHub MCP write. ⛔ No issue filing: platform findings go in the report, and the seat files them.
The write budget is tight, so make two writes: the PR and the report.
Report: the os-dev-report JSON, posted as a comment on this card and sent as the final message to the seat thread. List each row as one of: fixed, already fixed, or platform-only (with the evidence). ⛔ Start editing only after the newest Claim: on this card names your branch. Push work in progress at each milestone, so a stop loses nothing.
Scope notes
Rows 1–9 are form and record-page metadata. Take before and after screenshots per row. Read project memories hotcrm-browser-pass-howto and hotcrm-console-18-2-facts first.
Row 10 (record titles) is measure-and-propose. ⛔ Do not change any object's nameField or display_title in this PR.
Moving Contact Email to the first tab (row 1) does not break the lead-conversion or import tests, which name form sections. Run them.
Serial constraints:#2368 edits the case public form and the case hook's guest path. Row 6 (Case Type on the case form) touches case.view.ts. Keep your case edits to the internal form layout, and merge origin/main before you finish.
Claim: PM loop round 1
Session: session_01J8VVAnPkgK6CWrJa6t7Fk5
Account: huangyiirene
Branch: claude/project-thread-0snsgp
Worktree: hotcrm-issue-2369 (card thread; branch is platform-designated, the measured platform exception to claude/issue-N-slug; implementing session session_01J8VVAnq1x2jshPfejjsKuT)
Domain: repo:hotcrm (single-lane repo, no domain:*)
Seat: repo:hotcrm#1
File surface: form and record-page metadata for the rows' objects (contact, lead, opportunity, quote/line item, product, case internal form, knowledge article, task/event, account): views, pages, highlight fields, field help/defaults/prefill, translation entries for changed labels (no file moves), test/ for them, the in-app manual pages that describe a changed surface (en + zh), one changeset; ⛔ any nameField / display_title change (row 10 is measure-and-propose); ⛔ the case public form and guest path (#2368) (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (sister repo, hand-derived), effort: the order asked high; the thread runs at the project default, which only the maintainer sets
Clause-②: no
Metadata-protocol: none
Responsibility: #2369 UX card 5: form/record-page rows 1–9 fixed or reported, row 10 measured with options for the maintainer
Thread-read: 6488305954
Premise: holds — re-read at claim on hotcrm origin/main52083a5 (order comment 6488305954)
Serial constraints cleared: #2368 edits the case public form and the case hook's guest path; keep case edits to the internal form layout and merge origin/main before finishing
{
"issue": 2369,
"status": "done",
"branch": "claude/project-thread-0snsgp",
"pr": "https://github.com/objectstack-ai/hotcrm/pull/2380",
"session": "session_01J8VVAnq1x2jshPfejjsKuT",
"premise_still_valid": true,
"summary": "Rows 1-9 measured on origin/main (52083a5, then 66c63cb after #2368, merged c362ccd after #2370) and handled; row 10 measured only, no nameField or display_title changed. Row 1 fixed: contact Identity section 3 columns with email and phone, error shows on the first tab. Row 2 fixed: lead name fields on one row, rating description says blank is auto-scored. Row 3 partly fixed: description names where the email goes; title already named the action; 'Confirm' button and the recipient address are platform. Row 4 platform for the prefill (no fill-on-pick on 18.2.0 forms; price still filled after save from crm_product.list_price, the field #2324 phase 1 keeps); product highlightFields lead with List Price so the line-item picker shows it. Row 5 fixed: highlightFields on account, quote, product, campaign, article carry what the title does not; relatedListColumns pin the related lists that borrowed them. Row 6 fixed on the internal case form (type added; public form untouched, #2368) and the page (is_sla_violated out of the strip, record:alert case_sla_breached when true). Row 7 fixed except a persistent voted state: content group first, counts in the strip, vote body returns outcome + tally, outcomeMessages (${result.x}). Row 8 platform for the prefill (dialog gets no row); attendee_contacts helpText says the record's contact or lead is added; UTC date+time split untouched. Row 9 fixed: opportunity Quotes/Products/Open Tasks and case Open Tasks open. New text in en/zh-CN/ja-JP/es-ES; in-app manual en+zh (sales activities, leads, service cases, knowledge). Screenshots before/after in project files issue-2369/ (81 PNGs).",
"tests": "pnpm verify at 252b0e6 (after merging c362ccd): Test Files 253 passed (253), Tests 4873 passed | 1 skipped, EXIT 0. First full run before the merge red on lint field-group-shadowed (campaign highlights hid the schedule group), fixed. New test/forms-record-pages-2369.test.ts (18): all 18 red with src at origin/main. knowledge-feedback adds 'returns the outcome and the article's tally for the toast': red with the action file at origin/main. case-create-form-narrowing: detail.alert surface, type creator-authorable. Lead-conversion and import tests pass.",
"gates": "local: pnpm verify (validate, typecheck, lint, hygiene, hygiene:tokens, build, test) EXIT 0 at 252b0e6. CI check-runs on #2380: not yet reported at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "read only: issue_read (get, get_comments), pull_request_read get; subscribe_pr_activity on #2380. No write tool.",
"api_writes": "2 — relay pr_create (objectstack-ai/hotcrm#2380, assignee huangyiirene, read back identical); post-stamped comment on #2369 (this report)",
"open_questions": [
"Row 10, record titles — pick one; measured on 18.2.0 as shipped and in a fresh scratch boot with the six titles (account, product, quote, campaign, case, article) set to A (name alone) and B (name first, 'Globex Industries · ACC-000002'). Shots: r10-keep-*, r10-nameonly-*, r10-namefirst-* in project files issue-2369/.",
"Import matcher (@objectstack/core resolveRef): tries id, lookup displayField, target nameField, then name/title/label/full_name/email/username; >1 hit = reference_ambiguous; a bare autonumber matches nothing. Measured: today 'ACC-000012 - Probe Solo' and 'Probe Solo' link; under A and B the old title cell is reference_not_found; under B 'Probe Solo · ACC-000012' links; under A a shared name is reference_ambiguous on every object whose name is not unique (product, quote, campaign, case subject, article title; account name is unique per org).",
"Search: $search reads searchableFields (name + number), finds by either under all three. Sort on display_title: by number today, by name under A and B. ⌘K, lookup cells, related-list cells, pickers and record_link show the title.",
"Cost: A turns 11 tests red in 4 files, B 15 in 4 (stored-record-titles, hooks-runtime-sales, line-item-product-title, quote-accepted-lookups); quote.hook.ts writes 'QTE-… - name' into its freeze refusals itself; line-item product_title mirror and price-book entry names copy the product title; backfill-record-titles.ts and backfill-line-product-title.ts must run on upgrade. Old export files stop matching under A and B.",
"Options: Keep (name cut: 'ACC-000002 - Globex Ind…'); A name alone, number as a column and picker subtitle ('Globex Industries' / 'Account Number: ACC-000002'); B name first ('Globex Industries · ACC-00…', number cut, title stays unique). Recommendation: B for all six; A only for crm_account. The platform change in out_of_scope_findings (matcher tries unique fields) would make A safe everywhere."
],
"out_of_scope_findings": [
"class: a · reach: action param dialog opened from the record header or the list ⋮ menu gets no row: {{ record.x }} in description and helpText prints raw, defaultFromRow fills nothing (send_email, log_call on contact/case/opportunity) · dedupe: action dialog record context defaultFromRow",
"class: a · reach: action param dialog submit button is always 'Confirm' (actionDialog.confirm), no metadata key for the action's own verb · dedupe: action dialog confirm button label",
"class: a · reach: a form lookup pick cannot fill another field (quote line product -> unit price); the price arrives only after save · dedupe: lookup pick autofill form",
"class: a · reach: record:highlights draws a boolean false as a red '× No' and true as a green '✓ Yes' whatever the field means (is_sla_violated); record:details draws a neutral circle · dedupe: highlights boolean semantic colour",
"class: a · reach: a record_header action button cannot show per-reader state (knowledge article Helpful after you voted) · dedupe: action button per user state",
"class: b · reach: import lookup matcher (@objectstack/core resolveRef) never tries an object's unique fields, so 'ACC-000012' names nothing and any title change breaks old export files · dedupe: import reference match autonumber unique field",
"class: c · reach: outcomeMessages interpolate ${result.x} while successMessage takes {{ result.x }}; a {{ }} hole in outcomeMessages prints raw with no lint · dedupe: outcomeMessages placeholder syntax lint"
]
}
Merged 2026-10-29T06:41Z as d963020, from head 252b0e6, which is the head the seat ACCEPTed (6489853841).
main CI on d963020
GET /commits/d963020/check-runs, GET /actions/runs?head_sha=
All 5 check runs and all 4 workflow runs success.
State:
Fixed: the contact create form holds every required field on its first tab, and name parts share a row on contact and lead. Lead Score says a blank is scored. Send Email says where the mail goes. Record header strips no longer repeat the title, and the panels that fell back to them keep their columns. A breached case shows an SLA Breached alert in place of a red "No" pill, and staff case forms gain Type. An article vote says whether it recorded, changed or repeated, with the tally. Every opportunity panel, and the case's Open Tasks panel, opens on arrival.
Platform gaps: the dialog's row context and verb, lookup-pick autofill, boolean highlight colours, per-reader button state, the import matcher's unique fields and the ${} vs {{ }} lint are objectstack#27434.
Row 10 (record titles): measured only (keep / A name alone / B name first). It is with the maintainer, and the seat recommends B. A follow-up PR closes this card, so it stays open and keeps pm:dispatched.
Activity
objectstack-fleet commentedon Oct 29, 2026
Dispatch order · #2369 ·
repo:hotcrmseat 1 ·huangyiirene· sessionsession_01J8VVAnPkgK6CWrJa6t7Fk5· 2026-10-29T04:53ZThis order takes the slot freed by #2367 (PR #2377). It is UX card 5 in Irene's queue order: #2371 → #2366 → #2367 → #2368 → #2369 → #2370. The card body is the spec. #2368 holds the other slot.
Container & model: M ·
mode:thread· modelTIER_DEFAULT· efforthigh(the thread runs at the project default).Role file:
objectstack-ai/objectstack.claude/agents/os-dev.md. Read it in full. hotcrmAGENTS.mdbinds. Re-measure every row onorigin/main(52083a5or later).Writes:
pr_create: draft, basemain,assignees: [huangyiirene].scripts/pm/post-stamped.mjs.Report: the
os-dev-reportJSON, posted as a comment on this card and sent as the final message to the seat thread. List each row as one of: fixed, already fixed, or platform-only (with the evidence). ⛔ Start editing only after the newestClaim:on this card names your branch. Push work in progress at each milestone, so a stop loses nothing.Scope notes
hotcrm-browser-pass-howtoandhotcrm-console-18-2-factsfirst.nameFieldordisplay_titlein this PR.record_linkfields.open_questions. The seat takes the choice to Irene.PM mechanism assumptions
Serial constraints: #2368 edits the case public form and the case hook's guest path. Row 6 (Case Type on the case form) touches
case.view.ts. Keep your case edits to the internal form layout, and mergeorigin/mainbefore you finish.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
Claim: PM loop round 1
Session:
session_01J8VVAnPkgK6CWrJa6t7Fk5Account:
huangyiireneBranch:
claude/project-thread-0snsgpWorktree:
hotcrm-issue-2369(card thread; branch is platform-designated, the measured platform exception toclaude/issue-N-slug; implementing sessionsession_01J8VVAnq1x2jshPfejjsKuT)Domain:
repo:hotcrm(single-lane repo, nodomain:*)Seat:
repo:hotcrm#1File surface: form and record-page metadata for the rows' objects (contact, lead, opportunity, quote/line item, product, case internal form, knowledge article, task/event, account): views, pages, highlight fields, field help/defaults/prefill, translation entries for changed labels (no file moves),
test/for them, the in-app manual pages that describe a changed surface (en + zh), one changeset; ⛔ anynameField/display_titlechange (row 10 is measure-and-propose); ⛔ the case public form and guest path (#2368) (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT (sister repo, hand-derived),effort: the order asked high; the thread runs at the project default, which only the maintainer setsClause-②: no
Metadata-protocol: none
Responsibility: #2369 UX card 5: form/record-page rows 1–9 fixed or reported, row 10 measured with options for the maintainer
Thread-read: 6488305954
Premise: holds — re-read at claim on hotcrm
origin/main52083a5(order comment 6488305954)Serial constraints cleared: #2368 edits the case public form and the case hook's guest path; keep case edits to the internal form layout and merge origin/main before finishing
feat(ux): forms, record pages and record headers for #2369 rows 1-9
objectstack-fleet commentedon Oct 29, 2026
repo:hotcrm#1 · huangyiirene · session_01J8VVAnq1x2jshPfejjsKuT
os-dev-report
{ "issue": 2369, "status": "done", "branch": "claude/project-thread-0snsgp", "pr": "https://github.com/objectstack-ai/hotcrm/pull/2380", "session": "session_01J8VVAnq1x2jshPfejjsKuT", "premise_still_valid": true, "summary": "Rows 1-9 measured on origin/main (52083a5, then 66c63cb after #2368, merged c362ccd after #2370) and handled; row 10 measured only, no nameField or display_title changed. Row 1 fixed: contact Identity section 3 columns with email and phone, error shows on the first tab. Row 2 fixed: lead name fields on one row, rating description says blank is auto-scored. Row 3 partly fixed: description names where the email goes; title already named the action; 'Confirm' button and the recipient address are platform. Row 4 platform for the prefill (no fill-on-pick on 18.2.0 forms; price still filled after save from crm_product.list_price, the field #2324 phase 1 keeps); product highlightFields lead with List Price so the line-item picker shows it. Row 5 fixed: highlightFields on account, quote, product, campaign, article carry what the title does not; relatedListColumns pin the related lists that borrowed them. Row 6 fixed on the internal case form (type added; public form untouched, #2368) and the page (is_sla_violated out of the strip, record:alert case_sla_breached when true). Row 7 fixed except a persistent voted state: content group first, counts in the strip, vote body returns outcome + tally, outcomeMessages (${result.x}). Row 8 platform for the prefill (dialog gets no row); attendee_contacts helpText says the record's contact or lead is added; UTC date+time split untouched. Row 9 fixed: opportunity Quotes/Products/Open Tasks and case Open Tasks open. New text in en/zh-CN/ja-JP/es-ES; in-app manual en+zh (sales activities, leads, service cases, knowledge). Screenshots before/after in project files issue-2369/ (81 PNGs).", "tests": "pnpm verify at 252b0e6 (after merging c362ccd): Test Files 253 passed (253), Tests 4873 passed | 1 skipped, EXIT 0. First full run before the merge red on lint field-group-shadowed (campaign highlights hid the schedule group), fixed. New test/forms-record-pages-2369.test.ts (18): all 18 red with src at origin/main. knowledge-feedback adds 'returns the outcome and the article's tally for the toast': red with the action file at origin/main. case-create-form-narrowing: detail.alert surface, type creator-authorable. Lead-conversion and import tests pass.", "gates": "local: pnpm verify (validate, typecheck, lint, hygiene, hygiene:tokens, build, test) EXIT 0 at 252b0e6. CI check-runs on #2380: not yet reported at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "read only: issue_read (get, get_comments), pull_request_read get; subscribe_pr_activity on #2380. No write tool.", "api_writes": "2 — relay pr_create (objectstack-ai/hotcrm#2380, assignee huangyiirene, read back identical); post-stamped comment on #2369 (this report)", "open_questions": [ "Row 10, record titles — pick one; measured on 18.2.0 as shipped and in a fresh scratch boot with the six titles (account, product, quote, campaign, case, article) set to A (name alone) and B (name first, 'Globex Industries · ACC-000002'). Shots: r10-keep-*, r10-nameonly-*, r10-namefirst-* in project files issue-2369/.", "Import matcher (@objectstack/core resolveRef): tries id, lookup displayField, target nameField, then name/title/label/full_name/email/username; >1 hit = reference_ambiguous; a bare autonumber matches nothing. Measured: today 'ACC-000012 - Probe Solo' and 'Probe Solo' link; under A and B the old title cell is reference_not_found; under B 'Probe Solo · ACC-000012' links; under A a shared name is reference_ambiguous on every object whose name is not unique (product, quote, campaign, case subject, article title; account name is unique per org).", "Search: $search reads searchableFields (name + number), finds by either under all three. Sort on display_title: by number today, by name under A and B. ⌘K, lookup cells, related-list cells, pickers and record_link show the title.", "Cost: A turns 11 tests red in 4 files, B 15 in 4 (stored-record-titles, hooks-runtime-sales, line-item-product-title, quote-accepted-lookups); quote.hook.ts writes 'QTE-… - name' into its freeze refusals itself; line-item product_title mirror and price-book entry names copy the product title; backfill-record-titles.ts and backfill-line-product-title.ts must run on upgrade. Old export files stop matching under A and B.", "Options: Keep (name cut: 'ACC-000002 - Globex Ind…'); A name alone, number as a column and picker subtitle ('Globex Industries' / 'Account Number: ACC-000002'); B name first ('Globex Industries · ACC-00…', number cut, title stays unique). Recommendation: B for all six; A only for crm_account. The platform change in out_of_scope_findings (matcher tries unique fields) would make A safe everywhere." ], "out_of_scope_findings": [ "class: a · reach: action param dialog opened from the record header or the list ⋮ menu gets no row: {{ record.x }} in description and helpText prints raw, defaultFromRow fills nothing (send_email, log_call on contact/case/opportunity) · dedupe: action dialog record context defaultFromRow", "class: a · reach: action param dialog submit button is always 'Confirm' (actionDialog.confirm), no metadata key for the action's own verb · dedupe: action dialog confirm button label", "class: a · reach: a form lookup pick cannot fill another field (quote line product -> unit price); the price arrives only after save · dedupe: lookup pick autofill form", "class: a · reach: record:highlights draws a boolean false as a red '× No' and true as a green '✓ Yes' whatever the field means (is_sla_violated); record:details draws a neutral circle · dedupe: highlights boolean semantic colour", "class: a · reach: a record_header action button cannot show per-reader state (knowledge article Helpful after you voted) · dedupe: action button per user state", "class: b · reach: import lookup matcher (@objectstack/core resolveRef) never tries an object's unique fields, so 'ACC-000012' names nothing and any title change breaks old export files · dedupe: import reference match autonumber unique field", "class: c · reach: outcomeMessages interpolate ${result.x} while successMessage takes {{ result.x }}; a {{ }} hole in outcomeMessages prints raw with no lint · dedupe: outcomeMessages placeholder syntax lint" ] }objectstack-fleet commentedon Oct 29, 2026
Landed (rows 1–9) ·
repo:hotcrmseat 1 · sessionsession_01J8VVAnPkgK6CWrJa6t7Fk5· 2026-10-29T07:08ZGET /pulls/2380d963020, from head252b0e6, which is the head the seat ACCEPTed (6489853841).mainCI ond963020GET /commits/d963020/check-runs,GET /actions/runs?head_sha=success.State:
${}vs{{ }}lint are objectstack#27434.pm:dispatched.Generated by Claude Code