Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions .changeset/objectstack-17-5-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
'hotcrm': minor
---

Upgrade the ObjectStack platform to 17.5.0

All 21 `@objectstack/*` dependencies move 17.4.0 → 17.5.0 together — 20 in
`dependencies`, `@objectstack/formula` in `devDependencies` — pinned exact, no
caret. `specVersion` and `engines.protocol` follow to `^17.5.0`, in
`objectstack.manifest.json` and in the stack manifest in `objectstack.config.ts`.
`pnpm-lock.yaml` was regenerated by `pnpm install` against the public registry:
726 `packages:` entries before, 725 after. 54 `@objectstack/*` and
`create-objectstack` keys moved 1:1, and `@objectstack/plugin-reports` left the
tree because the platform removed the saved-report stack. 15 third-party keys
moved: the eleven `better-auth` / `@better-auth/*` packages 1.7.2 → exact 1.7.3,
`nodemailer` 9.0.5 → 10.0.12 (a major, taken by `@objectstack/plugin-email` for
GHSA-6vj9-mwq6-2f5v), `zod` 4.4.3 → 4.6.5, `@xmldom/xmldom` 0.9.11 → 0.9.12, and
`yaml@2.9.0` dropped. `hono@4.13.11` was added next to `hono@4.13.3`, which the
MCP SDK still resolves through the kept lock entry.

The platform changes that reached this app:

- **Dashboard widgets no longer carry chart structure in `chartConfig`.** On a
dataset-bound widget, `@objectstack/spec@17.5.0` refuses `chartConfig.type`,
`xAxis`, `yAxis` and `series`. The widget's own `type`, `dimensions` and
`values` were always what the renderer read, so the stack would not load with
them. 34 keys are removed across the Sales Activity, CRM Overview, Sales
Performance and Service Overview dashboards. Every removed `type` and `field`
agreed with the widget's own binding, so no chart changes family or plots a
different column. **What readers will see:** the eight bar and area charts that
had axis titles (*Rep*, *Week*, *Month*, *Revenue*, *Channel*, …) now render
without them. The platform offers no replacement for an axis title on a
dataset-bound widget. Value formatting still comes from each measure's own
`format`.
- **The *Close Case* screen offers a real knowledge-article picker.** 17.5.0 adds
`reference` to screen fields and requires it on a `lookup` screen field. On
17.4.0, *Resolved by Article (optional)* rendered as a plain text box asking
for an article id. The field now declares `reference: 'crm_knowledge_article'`,
the same target as the case's own `resolved_by_article` lookup. The
"Knowledge article id" placeholder is gone with the text box.
- **The Sales Home KPI cards state their filters as rule arrays.** The four
`object-metric` cards (*Revenue (Won)*, *Deals Won*, *Pipeline Value*, *Open
Leads*) wrote `filter` as a record. From 17.5.0, page and `object-*` filters
take only the `ViewFilterRule` array, and the platform converted the record
form on load. The source now says it in the accepted shape, as
`os migrate meta --from 17` lists. The numbers are unchanged.
- **Decision nodes are now exclusive by default. HotCRM needs no edit.** An
edge-branched `decision` with no `mode` now takes the first out-edge whose
condition holds, not every one of them. `os migrate meta --from 17` offers
`mode: 'inclusive'` on 13 of this app's decisions. Each of the 13 was checked
by hand, and in every one the out-edge conditions are exact complements, so
first-match runs exactly what every-true-edge ran. The key is deliberately not
written, as the platform's own migration note prescribes for partitioned
branches.
- **Scheduled flows are off until the deployment turns them on.** 17.5.0 runs
package-authored scheduled work only when `OS_AUTOMATION_SCHEDULED_WORK_ENABLED`
is set. HotCRM ships nine scheduled flows: contract and quote expiry, campaign
completion, forecast snapshot, stalled-deal alert, contract renewal, case SLA
monitor, task due reminder and demo bootstrap. **An operator who upgrades and
sets nothing runs none of them.** The admin *Automation* page now says so in
all three locales. Under the `isolated` posture a scheduled flow must also name
its organization, and these do not.
- **Six pages drop `assignedProfiles`. Who can open them does not change.**
`@objectstack/spec@17.5.0` removes `page.assignedProfiles` and refuses it. No
renderer, route or metadata read on 17.4.0 ever read the key, so the six pages
that set it were already open to every caller who could reach them. The pages
are *App Launcher*, *Sales Home*, *Utility Bar*, *Lead Detail*, *Opportunity
Detail* and *Case Detail*. Deleting the key keeps that behaviour. The platform
gates what a page shows through the object permission sets, which HotCRM
already declares. Two tests checked the retired key and are deleted with it.
One was "assignedProfiles name real profiles" in
`test/metadata-references.test.ts`. The other was "every related list is
readable by every profile its page is assigned to" in
`test/authorization-coverage.test.ts`. With no page able to declare the key,
both ran over zero pages and could no longer fail.
- **The contact form section *Account & Role* is now *Account & Title*.**
17.5.0 adds the author-time rule `security-role-word`: "role" is a reserved
word in labels, because the platform no longer has a Role concept. The section
holds the contact's owner, account, job title and department, so the English
label now says *Title*. The Chinese, Japanese and Spanish labels already said
"job title" and are unchanged.
- **Detail pages and Sales Home carry translated copy for nested components.**
From 17.5.0, `os lint` checks the translation of every component in a page's
tree, not only the top-level ones. There are 14 new keys in each of zh-CN,
ja-JP and es-ES:
- the Account Detail title and subtitle, and its discussion panel;
- the details sections of Case, Lead and Opportunity;
- the Lead Detail related, activity and field-history panels;
- the three "My …" lists on Sales Home.

These labels name the components. In a zh-CN browser check of Lead Detail on
17.5.0 none of them is drawn as visible text: the visible headings come from
the tab items and the object's field groups, which were already translated.
So readers should see no change. The keys exist to satisfy the new lint rule.
`os i18n extract` scaffolds page keys only with `--no-objects-only`, since its
default covers objects alone. The i18n gate's failure hint now gives that
command instead of the bare one.
- **One driver test reads the withheld filter diagnostic.** 17.5.0's SQL
drivers no longer put caller-supplied operator and field names in the thrown
message of a refused filter. The full text rides on the error, and
`withheldFilterDiagnosticOf` reads it. The retired-`$regex` premise test now
checks two things. The public message says RETIRED and does not name the
operator. The withheld diagnostic still names `$regex`.
- **Claims that named 17.4.0 as the current pin are re-scoped**, not renumbered,
across source comments, tests and maintainer docs. Each now dates itself to the
pin it was measured on. None was re-measured on 17.5.0 in this change. Four
stale "current pin 17.3.0" claims that the 17.4.0 sweep missed are re-scoped
the same way.

**Known issue on 17.5.0, fixed upstream but not yet released.** The *Products*,
*Knowledge Articles* and *Forecasts* default lists show
`Unknown field '[object Object]'` (`INVALID_FIELD`) in every group instead of
rows. On 17.5.0 the
console fetches grouped rows from the server, and the query it builds for a
view whose `columns` are objects sends the objects instead of field names. The
same views work on 17.4.0. HotCRM's views are valid and are left unchanged. The
fix is objectui#11105 (PR objectui#11119, merged). It reaches HotCRM with the
first platform release whose `@objectstack/console` carries it, and HotCRM then
needs only a pin bump.
2 changes: 2 additions & 0 deletions content/docs/administration/automation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ See [Customization › Extending Objects](/docs/customization/extending-objects)

Time-based automation is implemented as **scheduled flows** — flows whose start node carries a cron schedule. The nine `Schedule` rows above are the complete set; there is no separate scheduled-job metadata to look for. They run via the job service, so the `triggers` capability is paired with `job` (both ship in the default slate).

> **From ObjectStack 17.5.0, scheduled flows are off until the deployment turns them on.** The platform runs package-authored scheduled work only when the deployment sets `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true` (`1`, `on` and `yes` also count). Without it, none of the nine flows above runs — no contract or quote expiry, no SLA monitor, no reminders, no forecast snapshots — and `os doctor` prints the effective value. Under the `isolated` tenancy posture a scheduled flow must also name the organization it acts as; these nine do not, so they are not armed there.

Date-driven field logic that needs no orchestration — defaulting a quote's expiration date, freezing an expired/accepted quote, deriving a forecast period — lives in lightweight **object hooks** (`beforeInsert` / `beforeUpdate`) rather than a scheduled sweep.

The division of labour on forecasts is worth calling out, because it is the pattern to copy: the **Forecast Snapshot** flow decides *who* gets a snapshot and *what the totals are*, while the forecast object's hook decides *which calendar period the snapshot belongs to*. A cron expression can say "every night"; it cannot say "the first day of this quarter", so that boundary is derived once, by the object, for every writer.
Expand Down
2 changes: 2 additions & 0 deletions content/docs/administration/automation.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ description: 验证规则、流程、计划作业与审批 —— 无需你动

基于时间的自动化以 **计划类流程** 实现 —— 即起始节点带 cron 计划的流程。上表中九个 `计划` 行就是全部;不存在另一套需要另找的「计划作业」元数据。它们通过 job 服务运行,因此 `triggers` 能力需与 `job` 搭配(两者都在默认能力集中)。

> **自 ObjectStack 17.5.0 起,计划类流程默认关闭,需由部署方开启。** 平台只在部署设置了 `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true`(`1`、`on`、`yes` 同样有效)时,才运行包内编写的计划类工作。不设置时,上表九个计划类流程一个都不会运行 —— 没有合同与报价的自动过期、没有 SLA 监控、没有提醒、没有预测快照;`os doctor` 会打印当前生效的取值。在 `isolated` 租户隔离模式下,计划类流程还必须声明它代表哪个组织运行;这九个流程都没有声明,因此在该模式下不会被挂载。

无需编排的日期驱动字段逻辑 —— 例如为报价设置默认过期日、冻结已过期/已接受的报价、推导预测周期 —— 放在轻量的 **对象钩子**(`beforeInsert` / `beforeUpdate`)中,而非计划清扫。

预测这一块的分工值得单独点出来,因为它正是要照抄的模式:**预测快照** 流程决定*谁*会拿到快照、*各项合计是多少*,而预测对象的钩子决定*这条快照属于哪个日历周期*。一条 cron 计划说得出「每晚」,却说不出「本季度的第一天」—— 所以这个周期边界由对象统一推导一次,供每个写入方共用。
Expand Down
2 changes: 2 additions & 0 deletions content/docs/administration/automation.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ description: 驗證規則、流程、排程作業與審批 —— 無需你動

基於時間的自動化以 **排程類流程** 實作 —— 即起始節點帶 cron 排程的流程。上表中九個 `排程` 行就是全部;不存在另一套需要另找的「排程作業」中繼資料。它們透過 job 服務執行,因此 `triggers` 能力需與 `job` 搭配(兩者都在預設能力集中)。

> **自 ObjectStack 17.5.0 起,排程類流程預設關閉,需由部署方開啟。** 平台只在部署設定了 `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true`(`1`、`on`、`yes` 同樣有效)時,才執行套件內撰寫的排程類工作。未設定時,上表九個排程類流程一個都不會執行 —— 沒有合約與報價的自動到期、沒有 SLA 監控、沒有提醒、沒有預測快照;`os doctor` 會列印目前生效的取值。在 `isolated` 租戶隔離模式下,排程類流程還必須宣告它代表哪個組織執行;這九個流程都沒有宣告,因此在該模式下不會被掛載。

無需編排的日期驅動欄位邏輯 —— 例如為報價設定預設過期日、凍結已過期/已接受的報價、推導預測週期 —— 放在輕量的 **物件鉤子**(`beforeInsert` / `beforeUpdate`)中,而非排程清掃。

預測這一塊的分工值得單獨點出來,因為它正是要照抄的模式:**預測快照** 流程決定*誰*會拿到快照、*各項合計是多少*,而預測物件的鉤子決定*這筆快照屬於哪個日曆週期*。一條 cron 排程說得出「每晚」,卻說不出「本季的第一天」—— 所以這個週期邊界由物件統一推導一次,供每個寫入方共用。
Expand Down
2 changes: 1 addition & 1 deletion content/docs/sales/contacts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The contact detail screen has 7 collapsible sections:
| Section | Fields |
| --- | --- |
| **Identity** | Salutation, first name, last name, full name, profile picture |
| **Account & Role** | Contact owner, account, job title, department |
| **Account & Title** | Contact owner, account, job title, department |
| **Buying Centre** | Buying function, attitude to us, relationship strength — see [The Buying Centre](./buying-centre) |
| **Contact Information** | Email, phone, mobile |
| **Mailing Address** | Mailing street, city, state/province, postal code and country — five separate fields |
Expand Down
4 changes: 4 additions & 0 deletions content/docs/whats-new.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ http://localhost:4001 and create the first admin account.
refuses the load up front with a structured diagnostic instead of failing deep
in a schema parse.

Since 3.1.0 was cut, the app has moved to **ObjectStack 17.5.0** (manifest range
`^17.5.0`). That move ships with the next HotCRM release; until then, 3.1.0 is the
release that runs on 17.4.0.

> **This page does not keep a second release table.** The release-by-release
> history is `CHANGELOG.md` in the repository, compiled from the `.changeset/`
> entry every pull request adds. The versions between v1.0 and 3.1.0 are
Expand Down
3 changes: 3 additions & 0 deletions content/docs/whats-new.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ HOTCRM_TOKEN=... ./scripts/wow1-live-schema.sh
应用清单声明了与之匹配的 `^17.4.0` 协议区间,因此协议大版本不同的运行时会在
装载之初就带着结构化诊断拒绝加载,而不是在某次 schema 解析的深处才失败。

3.1.0 发布之后,应用已迁移到 **ObjectStack 17.5.0**(清单区间 `^17.5.0`)。这次迁移随下一个
HotCRM 版本发布;在那之前,运行在 17.4.0 上的发布版本仍是 3.1.0。

> **这一页不维护第二张发布表。** 逐版本的发布历史在仓库的 `CHANGELOG.md` 里,由每个
> 拉取请求补充的 `.changeset/` 条目编译而成。v1.0 与 3.1.0 之间的各个版本都记录在那里,
> 这里刻意不再重列一遍 —— 手工誊抄那份历史,正是这一节最终会宣称一个从来不曾存在过的
Expand Down
3 changes: 3 additions & 0 deletions content/docs/whats-new.zh-Hant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ HOTCRM_TOKEN=... ./scripts/wow1-live-schema.sh
應用清單宣告了與之相符的 `^17.4.0` 協定區間,因此協定大版本不同的執行時會在
載入之初就帶著結構化診斷拒絕載入,而不是在某次 schema 解析的深處才失敗。

3.1.0 發行之後,應用已遷移到 **ObjectStack 17.5.0**(清單區間 `^17.5.0`)。這次遷移隨下一個
HotCRM 版本發行;在那之前,執行在 17.4.0 上的發行版本仍是 3.1.0。

> **這一頁不維護第二張發行表。** 逐版本的發行歷史在儲存庫的 `CHANGELOG.md` 裡,由每個
> 拉取請求補上的 `.changeset/` 條目編譯而成。v1.0 與 3.1.0 之間的各個版本都記錄在那裡,
> 這裡刻意不再重列一遍 —— 手工謄抄那份歷史,正是這一節最終會宣稱一個從來不曾存在過的
Expand Down
2 changes: 1 addition & 1 deletion docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pnpm verify
| --- | --- |
| Node.js | `^22.11 \|\| ^24 \|\| >=26` |
| pnpm | `>=10.0.0` |
| ObjectStack packages | `17.4.0` |
| ObjectStack packages | `17.5.0` |
| Local dev port | `4001` |

Each row above is asserted against `package.json` (`engines`, the `@objectstack/*`
Expand Down
13 changes: 7 additions & 6 deletions docs/architecture/module-split-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ navigationContributions: [
```

The shape was first read off `ManifestSchema` in `@objectstack/spec` 17.2.0 — the
version this repo pinned when this plan was measured — and RE-READ on the current pin,
17.4.0 since PR #1814 (#1807), during #1883:
version this repo pinned when this plan was measured — and RE-READ on 17.4.0 (PR #1814,
#1807), the pin during #1883:
`{ app, group?, priority, items }`, unchanged, with `priority` optional (it defaults to
`200`). ⚠️ One thing the 17.2.0 reading did not record: `app` is validated as a bare
snake_case identifier (`/^[a-z][a-z0-9_]*$/`), so the dotted manifest id used in the
Expand Down Expand Up @@ -358,9 +358,10 @@ would otherwise slug the heading to `#上游缺口--upstream-gaps` — the exact
[objectstack#14439](https://github.com/objectstack-ai/objectstack/issues/14439), in flight.
- Studio's writable verdict —
[objectstack#14430](https://github.com/objectstack-ai/objectstack/issues/14430).
- An objectstack release carrying both, and the version bump in this repository. HotCRM is
pinned to `@objectstack/*` 17.4.0 today (PR #1814, #1807; this plan's own measurements
were taken at the 17.2.0 pin and are labelled as such). Merged upstream is not the same as available
- An objectstack release carrying both, and the version bump in this repository. HotCRM was
pinned to `@objectstack/*` 17.4.0 when this was written (PR #1814, #1807) and moved to 17.5.0
after it (this plan's own measurements were taken at the 17.2.0 pin and are labelled as such).
Merged upstream is not the same as available
in the pin (AGENTS.md, *Platform Upgrades*).

**New, found by this analysis — for the PM to file upstream.** None of these is worked around
Expand Down Expand Up @@ -565,7 +566,7 @@ decisions left to the compile path and this ruling settles.
- **PSA** follows as `src/psa/` (`psa-module-plan.md`).

The three upstream blockers under *上游缺口* are no longer the reason to wait: the compile
path and per-package registration are carried by the 17.4.0 pin, and the Studio writable
path and per-package registration are carried from the 17.4.0 pin on, and the Studio writable
verdict does not gate a module booted from an artifact — the version table in
`psa-module-plan.md`, *Where PSA sits*, cites the changelog entries. PR 1 is still the
measurement that proves it here.
Expand Down
2 changes: 1 addition & 1 deletion objectstack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default defineStack({
// enforces that pairing against `objectstack.manifest.json` instead of
// trusting this comment, because two platform upgrades in a row (rc.2, then
// rc.3) moved the manifest and left this line behind (#728).
engines: { protocol: '^17.4.0' },
engines: { protocol: '^17.5.0' },
},

// ─── Platform capabilities this app needs ─────────────────────────
Expand Down
4 changes: 2 additions & 2 deletions objectstack.manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"$schema": "https://schemas.objectstack.dev/template-manifest.json",
"name": "hotcrm",
"specVersion": "^17.4.0",
"specVersion": "^17.5.0",
"engines": {
"protocol": "^17.4.0"
"protocol": "^17.5.0"
},
"manifestId": "app.objectstack.hotcrm",
"displayName": "HotCRM",
Expand Down
Loading
Loading