Skip to content

feat(forecast): title a forecast by its period and its owner, "Q4 2026 · Nina Reyes" - #2348

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-fckufj
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-fckufj

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread
Fixes #2242

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:预测记录的标题从「Q4 2026 (2026-10-01)」改为「Q4 2026 · Nina Reyes」(期间标签 · 负责人);没有负责人时只显示期间标签「Q4 2026」。负责人名字通过 owner_id 查找字段读取,不复制成文本列。
  • 为什么改:预测是「负责人 × 期间」一条,原标题只有期间,两个人的 Q4 预测在记录页、最近访问和搜索里分不出来([Decision] Which of the 33 UI and UX suggestions from the 2026-10-20 browser passes to adopt (seat: adopt 23, fold 4 into open cards, defer 6) #2194 T1-U08 裁决)。
  • 风险与回滚:只动一个存储标题表达式、两段注释、文档和测试。名字在保存时写入,用户改名后要等该预测下次保存才更新。回滚即还原本 PR。
  • 先看:src/sales/objects/forecast.object.ts(display_title 表达式及其注释),test/forecast-owner-title.test.ts。
  • 两分钟验证:pnpm vitest run test/forecast-owner-title.test.ts,预期 3 个用例通过;或在控制台打开任一预测,标题显示「Q4 2026 · Dev Admin」。
  • 需要你做的:无,席位落地。

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Documentation update

What changes

The title names the owner. crm_forecast.display_title (stored since #2230) now computes

record.?owner_id.?name.orValue("") == "" ? record.period_label : record.period_label + " · " + record.?owner_id.?name.orValue("")

the guarded form from the blocked report on #2242 (comment 6385293263, Resume), as the dispatch ruling requires. Two owners' Q4 2026 forecasts now read Q4 2026 · Nina Reyes and Q4 2026 · Omar Diaz; an ownerless one reads Q4 2026. The name is read one hop through the owner_id lookup when the engine computes the stored value, so reassigning a forecast re-titles it on that write. No text column copies the name.

Two stale comments corrected. forecast.object.ts said twice that "an expression cannot dot-walk a lookup (ADR-0072)". That is false on 18.1.0 and 18.2.0 (measured, below). Both now describe what the title does, including that a user rename reaches a forecast's title only at that forecast's next write. The note about string() versus text() is gone with the string() call it explained.

Docs. content/docs/sales/forecasting (en, zh-Hans, zh-Hant) gains two sentences in How periods are derived: the title, the ownerless form, and when it changes.

Changeset. .changeset/2242-forecast-title-owner.md (minor), with FROM → TO.

Tests

New test/forecast-owner-title.test.ts, 3 cases on the booted app through @objectstack/verify:

  • two owners' forecasts for the same quarter have different titles (Q4 2026 · Nina Reyes, Q4 2026 · Omar Diaz): the pin from the card's Done when;
  • a forecast with no owner is titled Q4 2026;
  • a by-id PATCH moving owner_id from Nina to Omar re-titles the row, so the name comes from the lookup.
Reverse check: the pin on the old title expression

The fix committed first (34af0c4). The expression was then swapped back to record.period_label + " (" + string(record.period_start) + ")" on disk (injected text counted present, the owner expression counted 0), the file restored with git checkout HEAD -- src/sales/objects/forecast.object.ts under a trap, and the restore proven by git diff HEAD empty and a blob-hash match.

MUT_EXIT=1
  × two owners' forecasts for the same quarter have different titles
  × a forecast with no owner is titled by its period label alone
  × the name comes from the lookup: handing the row to another owner re-titles it
  Tests  3 failed (3)
diff-HEAD-empty: yes · hash-match

Verification

At e41648a (after merging origin/main 0f17c6c): pnpm validate, typecheck, lint (objectstack lint --strict), hygiene, hygiene:tokens and build, all exit 0, and the full suite:

 Test Files  232 passed (232)
      Tests  4474 passed | 1 skipped (4475)

main has since moved to b65c403 (#2346, #2338 items 1–4; no file in common with this diff), so CI's merge ref is the reading for that tip. Before the first merge, at 6bf2397, the full suite read 230/230 files, 4447 passed, 1 skipped.

Fresh-boot measurement on the installed 18.2.0 (dispatch assumption 1)

Dev server, fresh SQLite file, --seed-admin, read after the seed-ownership claim settled:

run display_title forecasts ownerless crm_account ownerless (control) claimSeedOwnership failed lines
A main bbd375b Q2 2026 (2026-04-01) … 7 0 0 / 9 0
B this change Q2 2026 · Dev Admin … 7 0 0 / 9 0

On 18.1.0 row B read 7 ownerless (the blocked report). objectstack#25668 (in 18.2.0) re-owns seeded rows by id, and that write titles each seeded forecast with the admin it hands it to.

Browser check (dev admin, fresh seed, branch head)

Two Q4 2026 forecasts created through REST, one owned by the dev admin and one by a user named Nina Reyes (created through auth/admin/create-user):

  • Record header: Q4 2026 · Nina Reyes and Q4 2026 · Dev Admin.
  • Command palette (Ctrl+K), Recently viewed: Q4 2026 · Dev Admin, Q4 2026 · Nina Reyes, each tagged Forecast.
  • Command palette search for Q4 2026: each hit is titled with its owner.

Screenshots: project files issue-2242/ (01-nina-record, 02-admin-record, 04-search-recent, 05-search-q4).

Acceptance notes

  • Dispatch assumption 2, measured. The title recomputes on insert (the pin) and on a by-id update (the pin's reassignment case; also a PATCH of notes alone on a database written before this change turned Q2 2026 (2026-04-01) into Q2 2026 · Dev Admin).
  • No backfill change was needed. scripts/backfill-record-titles.ts re-saves only rows with no stored title. Store the 9 formula record titles as stored computed fields and backfill them, so import can match them and sort and search can use them (objectstack#24981 ruling A) #2230's stored titles are not released yet (.changeset/2230-stored-record-titles.md is still pending), so no released install holds the old stored form: an upgraded install's forecasts are untitled and that backfill titles them in the new form. Only a dev database created on main since Store the 9 formula record titles as stored computed fields and backfill them, so import can match them and sort and search can use them (objectstack#24981 ruling A) #2230 holds Q4 2026 (2026-10-01); measured on a copy of run A's database, the backfill reported crm_forecast: 7 row(s), 0 without a stored display_title, and each row takes the new title at its next save or on pnpm demo:reset. The changeset says so.
  • Rename is not live. Re-measured on 18.2.0: after a driver write renamed the owner to Nina Reyes-Park, the forecast still read Q4 2026 · Nina Reyes; the next by-id PATCH (commit_amount only) re-titled it. This is a stored-field property, recorded in the object comment and the docs, not changed here.
  • "In lookups" has no HotCRM surface. No HotCRM field is a lookup to crm_forecast, so there is no lookup picker that lists forecasts. The search side was checked in the command palette instead.
  • The All Forecasts list shows no title column. Its columns are Owner, Period Label, Period Start and the amounts, so the list already told owners apart; unchanged.

Generated by Claude Code

A forecast is one row per owner and period, so a title naming the period
alone left two reps' Q4 rows reading the same in Recent items and in a
lookup. The stored display_title now reads "Q4 2026 · Nina Reyes": the
owner's name is read one hop through the owner_id lookup, and a row with
no owner is titled by its period label alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAm8CmTiBvFKWqv9eZH
@vercel

vercel Bot commented Oct 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Oct 27, 2026 11:28pm UTC

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces labels Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Verdict: ACCEPT · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-27T23:32Z · head e41648a
Implemented-by: card thread session session_01J8VVAm8CmTiBvFKWqv9eZH (report 6456157127 on #2242) · Reviewed-by: the seat, against the 6-file diff and hotcrm origin/main 52a6fb2

  • Shape:
  • Ruling held:
    • display_title is 「Q4 2026 · 」, using the guarded expression from 6385293263: record.?owner_id.?name.orValue("") == "" ? record.period_label : record.period_label + " · " + ….
    • With no owner, the title is the period label alone and never ends in "· ".
    • The stale "cannot dot-walk a lookup" comments at both sites are corrected.
  • Mechanism assumptions:
  • Tests:
    • test/forecast-owner-title.test.ts pins the card's Done when: two owners' Q4 forecasts have different titles, plus the ownerless case.
    • The reverse check against the old expression is red, 3 of 3.
  • Gates:
    • At e41648a: the full suite passed (232/232 files, 4474 passed, 1 skipped), and validate, typecheck, lint, hygiene, hygiene:tokens and build all exited 0.
    • CI on e41648a: 9 of 10 check runs passed. Build and Test (22.x) is still running, and the merge queue re-tests the merged result.
  • Deviation noted: commit 34af0c4 carries a model-named trailer, and history is not rewritten.
  • Landing: non-governed. The seat flips the PR to ready and arms auto-merge in this stroke.

Generated by Claude Code

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

Labels

ci/cd CI plumbing and the verification pipeline documentation Improvements or additions to documentation metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

2 participants