Skip to content

docs(analytics): 把 reports 页剩余三节与 cubes 整页写到 src/ (#962, #965) - #975

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-962-965-analytics-truth
Aug 6, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-962-965-analytics-truth

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #962
Fixes #965

两单并单实施。仅文档,三语同步,src/ 零改动,@objectstack/* 版本未动,content/docs/releases/ 未动。

#962 — analytics/reports 的 Sales / Revenue / Marketing 三节

src/reports/ 一共发布 10 份报表(build 输出确认 10 Reports)。PR #954 之后页面上 4 份是真的(Lead 1 + Service 3),剩下三节的 13 个名字在 src/ 里零命中,而真实的 6 份从未在本页出现过。

真名错位两处(沿 #924 先例点名说清真身与所在):

  • Pipeline by Stage 有两个真身,都不是报表:src/dashboards/shared-widgets.ts 的未结管道漏斗磁贴(CRM Overview / Sales Performance / Executive Overview 三个仪表盘共用),以及真实报表 Opportunities by Stage 的图表标题(src/reports/opportunity.report.ts:15)。
  • Stale Opportunities 是 src/views/opportunity.view.ts 的列表视图 label(Stale 标签页),而且原表「14 天以上无活动」也是错的:该视图不施加任何 14 天筛选,它列出全部未结交易按 stage_entry_date 升序排;14 天属于 opportunity_stagnation 流程。

六份真实报表就位:Sales 节写四份 opportunity 报表;新的「💰 客户与收入报表」节收下 Accounts by Industry and Type 与 Customer Churn Signals(含三个块的逐块说明)。

逐条区分「可自建」与「连自建都不可能」(沿 #954 已落地的 Lead/Service 口径):

名字 判定
Forecast vs Actual 可自建 —— 但要用 forecast_metrics,且必须钉死周期
Win/Loss Analysis 可自建 —— win/loss reason 是维度,win_rate 等是已声明度量
Big Deals Won 可自建 —— Won Revenue + 金额阈值筛选
Renewal Pipeline 按原口径不可 —— 关联反向;但按 Deal Type = Existing Customer - Renewal 可自建
Sales Cycle Length 不可 —— 无时长列,days_in_stage 是查询后公式
Discount Approval Activity 不可 —— approval_status 无维度,折扣在行项目,无 dataset 读行项目
Contracts Expiring / Active Contracts by Product 不可 —— 无合同 dataset(真身是 Renewal Calendar 等视图)
Campaign ROI / Campaign Engagement 不可 —— 无 campaign dataset

同页《订阅与定时推送》第一条示例、《权限》一条、《给经理的提示》两条原本都点名幻影报表,一并收口。

#965 — analytics/cubes 整页

页面骨架是「四个内置 cube」,而本仓一个 cube 都没有声明:每个语义定义都是 src/datasets/ 下的 defineDataset(...),由分析服务内部编译成 cube(ADR-0021;objectstack.config.ts 明确不注册 analyticsCubes,test/analytics-integrity.test.ts 也把这一点写死)。整页改写为九个真实 dataset 的表 + 逐节核对:

平台侧主张不预判为假(沿 #950 Copilot 节先例):cube 拖放 UI、刷新模型与刷新状态屏、cube 访问审计,都写成「本仓无从判定,请对着自己的部署验证」,不做任何方向上的断言。可实测的应用侧另写:src/skills/ 六个技能没有一个点名 dataset/cube,Live Data Access 走的是平台对象工具(describe_object / aggregate_data …);新增 cube 在本仓等价于新增 src/datasets/*.dataset.ts(改源码 + 重新部署)。

前提复核(rule 6)

两单正文的主张基本成立,但 #965 有一处点名错误:它把 Win rate 列进「页面列的 measure 有相当一部分不在其中」的清单,而 opportunity_metrics 第 92 行确实声明了 win_rate(连同 won_count / lost_count / decided_count 三个分母半边)。本 PR 按源码把 Win rate 写在「今天够得着」的一侧。其余 stale-premise 逐条复核未发现问题。

验证

六道门全绿(flock 内串行,heap 4096):validate / typecheck / lint / hygiene / build / test 退出码均为 0;Test Files 71 passed, Tests 1631 passed | 1 skipped;build 输出 10 Reports 5 Dashboards。

守卫单跑:test/docs-conversion-rate-spelling.test.ts 4 passed(#923 的两条 carrier 断言均绿 —— reports 页 carrier 在 #954 的 Lead 节,未触;cubes 页 carrier 落在改写后的 Marketing 节里,保留了「转化率 / 轉化率」的自然表述)。test/docs-service-index-analytics.test.ts、test/docs-drift.test.ts、test/analytics-integrity.test.ts 同跑 67 passed,均不触。

反向验证(方向先判后跑):

  1. 预判 RED:把 cubes.zh-Hans 的「转化率」换成别的词 → 实测 1 failed | 3 passed,红在 carrier 断言。符合预判。
  2. 预判 GREEN(守卫盲区):把一行幻影报表名 Campaign ROI 表格行加回 reports.mdx → 实测四个套件 67 passed 全绿。符合预判 —— 两页在报表名/cube 名这一层没有任何守卫,唯一钉住的只有 转化率 carrier。本 PR 如实报告这一盲区,未新增守卫(会跨出两单范围)。

两次实验后均已还原,最终树与提交一致。


Generated by Claude Code

…d the whole cubes page to source (#962, #965)

#962: src/reports/ publishes ten reports; the Sales / Revenue / Marketing
sections named thirteen that exist nowhere, while the six real ones -
Accounts by Industry and Type, Customer Churn Signals and the four in
opportunity.report.ts - had never appeared on the page. Two of the thirteen
were real names on the wrong thing: Pipeline by Stage is the funnel tile
shared by three dashboards and the chart title of the real Opportunities by
Stage report, and Stale Opportunities is the Stale list view, which applies
no 14-day cut (it ranks open deals by stage_entry_date; the 14 days belong
to opportunity_stagnation). Each remaining name is judged against the
semantic layer: Forecast vs Actual, Win/Loss Analysis, Big Deals Won and the
renewal pipeline are a custom report away; Sales Cycle Length, Discount
Approval Activity and the three contract reports are not, because no dataset
reads crm_contract or the line items and days_in_stage is a post-query
formula. Marketing has no campaign dataset at all. The subscription example,
the permissions bullet and the manager tips no longer name absent reports.

#965: the page was built on four cubes that do not exist. This app declares
no cube - every semantic definition is a defineDataset() under src/datasets/
and the analytics service compiles each into its cube (ADR-0021;
objectstack.config.ts registers no analyticsCubes). The page now lists the
nine real datasets with their real dimensions and measures, folds Sales and
Pipeline into the single opportunity_metrics both described, and names per
item what is unreachable and why - weighted pipeline (Expected Revenue is a
record field, no measure), discount (a line-item percent), product/family,
account tier and size, team and region (positions and Billing Country, i.e.
sharing machinery), sub-month grains, snapshot dates. Marketing keeps no
claim it cannot support: the campaign records carry the spend, counters and
the ROI % / Response Rate % formulas, and nothing aggregates them.

Platform-side sections are left explicitly unclaimed rather than declared
false, per the #950 precedent: the cube UI, the refresh cadence and the cube
access log are runtime questions this repo cannot answer. What is stated is
the app-side half - no skill names a dataset or cube, and Live Data Access
answers through the platform's object tools, not a compiled cube.

All three locales. Documentation only; nothing under src/ changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 6, 2026 2:13pm

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review August 6, 2026 14:18
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 01da4a8 Aug 6, 2026
9 checks passed
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
…-ai#976, objectstack-ai#977) (objectstack-ai#987)

* docs(analytics): write the landing page to source and retire the cube vocabulary (objectstack-ai#976, objectstack-ai#977)

`content/docs/analytics/index.mdx` is the first screen a reader of the analytics
docs sees, and every count on it named something else. Six claims, all three
locales, rewritten against `src/`:

- 4 cubes (Sales, Pipeline, Service, Marketing) -> no cube of this app's own;
  the semantic layer is the nine datasets in `src/datasets/`, compiled into
  cubes by the analytics service (ADR-0021). The four names hit nothing.
- 4 dashboards -> five; Sales Activity was missing.
- "10+ standard reports across leads, deals, cases, contracts" -> exactly ten,
  and none of them is a contract report: no dataset reads `crm_contract`, so
  none can be built.
- a rep running *Pipeline by Stage* -> **Opportunities by Stage**, with the
  name's two real identities (a shared dashboard tile, a chart title) written
  the way PR objectstack-ai#975 wrote them on the reports page.
- the **Analytics** navigation group -> **Insights**, with the five entries the
  app pins to it; nothing in the sidebar is called Dashboards/Reports/Cubes.
- "the AI Copilot can read directly from cubes" -> what this app declares (no
  skill names a dataset, cube or measure; the data-question skill aggregates
  over records), leaving the platform side undecided rather than asserted.

The `/docs/ai-copilot/sales-copilot` link the issue reported as dangling is not:
objectstack-ai#589 / PR objectstack-ai#611 renamed the link text and the target page has been there
throughout. Left as is, and pinned.

Eight further pages carried the same retired vocabulary (objectstack-ai#977): whats-new now
lists the nine datasets; for-developers draws `datasets/` instead of the
`src/cubes/` directory removed in objectstack-ai#492; performance-and-limits and faq state the
refresh cadence the app really declares (each dashboard's own `refreshInterval`
- 60s / 180s / 300s) instead of an "every 5 min incremental, nightly full"
figure nothing in `src/` configures, and no longer promise a manual refresh
button no dashboard declares; pipeline-management, introduction, fork-hotcrm and
sandbox-and-releases were renames only. The glossary's definition of the cube
concept is deliberately untouched.

`test/docs-analytics-vocabulary.test.ts` derives every count and product name on
these pages from `src/` and fails when the app and the page disagree. Reverse
verification: 25 of its 39 rules go red on the pre-fix tree, green after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

* docs(analytics): name the forecast figures by their record and dataset labels

The landing page's Copilot section cited Commit / Quota / Closed / Pipeline as
figures on the forecast record. Three of the four are: closed_amount is labelled
**Closed Won** on crm_forecast and only becomes **Closed** as a measure on the
Forecast Metrics dataset. Say both, so the name a reader sees on the record and
the name they select in a report each appear where they belong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

---------

Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
…ll-up (objectstack-ai#985) (objectstack-ai#990)

The "Forecasting roll-up" list on content/docs/sales/pipeline-management.mdx
pointed at a "Sales Dashboard" and a "Pipeline Coverage" report holding a
"quarter x stage" matrix. Neither was findable as written.

sales_dashboard renders under the label "Sales Performance", which is also its
Sales sidebar entry (src/dashboards/sales.dashboard.ts:22,
src/apps/crm.app.ts:61). And pipeline_coverage_by_quarter is a matrix over
forecast_category (rows) by close_quarter (columns) with total_amount and
opp_count in each cell; stage appears only in the runtimeFilter that drops
closed deals, so it is not an axis at all
(src/reports/opportunity.report.ts:35-42).

Both lines now carry the real label plus the metadata identifier, in all three
locales, worded the same way as the entry PR objectstack-ai#975 landed on analytics/reports
so the two pages no longer contradict each other.


Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment