Skip to content

docs(forecasting): a supplied period_start is kept, not snapped (#748) - #1009

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-748-forecasting-boundary-wording
Aug 7, 2026
Merged

docs(forecasting): a supplied period_start is kept, not snapped (#748)#1009
yinlianghui merged 1 commit into
mainfrom
claude/issue-748-forecasting-boundary-wording

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #748

content/docs/sales/forecasting.mdx「How periods are derived」一节在相邻两句里教了推导的两半:先说「Supply a period_start to snapshot a specific quarter or month」,再说「Because the boundary is always computed, never typed, every snapshot for the same quarter lines up exactly」。两句合起来给读者的结论是:手填什么 period_start,系统都会把它吸附到日历边界。实现不是这样。

本 PR 只动 boundary 措辞这一个面。同页 #732 在决策箱的宣称(Copilot skill 写 forecast 记录 / 存 transcript 一族句子)一字未动。

一、真实行为取证

src/objects/forecast.hook.tsforecast_derive_period补空缺,从不改写随记录送进来的值:period_start 缺失时才走 startOfPeriod(period, anchor) 落到日历真实边界(hook.ts:72-82);已给则原样保留,只拿它去推 period_end / period_label(hook.ts:84-91)。

把真实 handler 跑起来(test/helpers/hook-harness.tsmakeCtxbeforeInsert),读回 input

{ period: 'quarter', snapshot_date: '2026-08-02' }
  => period_start 2026-07-01  period_end 2026-09-30  period_label 'Q3 2026'   # 算出来的
{ period: 'quarter', period_start: '2026-07-15' }
  => period_start 2026-07-15  period_end 2026-09-30  period_label 'Q3 2026'   # 原样保留
{ period: 'month',   period_start: '2026-08-17' }
  => period_start 2026-08-17  period_end 2026-08-31  period_label 'Aug 2026'

即:never typed 只对「没填」的那一半成立。手填一个季度中间的日期,行就以那个日期存下来,标签仍是 Q3 2026,而按周期取数的界面全都对不上它——this_quarter_forecasts 视图(src/views/forecast.view.ts:78-81)和销售仪表盘的 Quota Attainment by Repsrc/dashboards/sales.dashboard.ts:332)都是拿 period_start 去和 {current_quarter_start} 做等值匹配的。

另一半(「同一季度的快照严格对齐」)本身是真的,但成立的理由不是「边界从不由人填」,而是推导只在一个地方发生、自动写入方都不自己算forecast-snapshot.flow.tscreate_forecast 只送 period(该节点注释即如此写),src/data/revenue.seed.ts 全部用 Date.UTC(y, m, 1)。hook 自己的注释措辞是准确的("give the object a period and it lands on a calendar-true boundary or not at all"),英文页压缩成 never typed 时丢掉了这个前提。

二、措辞前后对照

三个语言版本(.mdx / .zh-Hans.mdx / .zh-Hant.mdx)同改,改动位置一致:第 33 行补一处限定、第 39 行重写、其后新增一段。

英文页

  • 33 行:Supply a period_start to snapshot…Supply a period_start — that period's own first day — to snapshot…
  • 39 行:Because the boundary is always computed, never typed, every snapshot for the same quarter lines up exactly — …Because that derivation lives in one place instead of in every writer, snapshots that leave period_start blank all land on the same boundary and line up exactly — … Every automated writer takes that route: the nightly sweep sends period and nothing else.
  • 新增一段:**What you do supply is kept exactly as you sent it.** —— 派生只补空缺、不改写;送 2026-07-15 就存 2026-07-15,标签 Q3 2026 但起点在季度中间;This Quarter 页签与 Quota Attainment by Rep 都按季度真正的第一天匹配,因此找不到它;手工或 API 写快照时请送该周期的第一天,或干脆不送。

zh-Hans(原句已是 因为周期边界始终是算出来的,而不是由每个写入方各自去填…——比英文页克制,但仍留着「始终是算出来的」)

  • 39 行 → 因为这套派生只发生在一个地方,而不是由每个写入方各自去算,所有没有自带 period_start 的快照都会落在同一条边界上,严格对齐——…自动写入方走的都是这条路:每晚的定时快照只提交 period,别的什么都不给。
  • 新增段落起句:**你自己填进来的值会被原样保留。** 术语随页内既有译法(派生 / 行 / 仪表盘 / 本季度)。

zh-Hant:同样改动,术语随页内既有译法(衍生 / 資料列 / 儀表板 / 本季 /「」引号)。

保留未动:第 41 行「never see a record labelled 2026-07-01 through ???」、以及 #732 一族句子。回调框(blockquote)数量三语仍相等,docs-drift 的 locale callout parity 规则不受影响。

三、守卫

单句措辞不值得造文字黑名单式守卫,docs-drift 现有规则面(flow cron 标签 / src/ 目录引用 / dashboard tile / callout parity / persona / version)也没有能低成本挂上去的面。改为在既有的 forecast_derive_period runtime 用例块里把行为钉住两条(test/hooks-runtime-service.test.ts):无 period_start 时吸附到日历边界;手填时逐字保留。哪天有人改成「手填也吸附」,第二条转红,这段新写的散文就必须跟着重写。

反向验证(先定方向再跑):预测「把 #748 option 3 的契约变更强行装进 hook(对手填 period_start 也做 startOfPeriod 吸附)→ 逐字保留那条转红、日历边界那条保持绿」。实测一致:

✓ … > snaps to the calendar boundary only when the caller sends no period_start (#748) 0ms
× … > keeps a hand-supplied period_start verbatim, mid-period and all (#748) 13ms
  Tests  1 failed | 91 passed (92)

改动已回退,src/ 无任何改动(git diff --stat 只含 3 个 docs 页 + 1 个测试文件 + changeset)。

需要说明的是:文档散文本身没有测试钉住,所以「把措辞改回去」不会让任何测试变红——这条不做虚报。转红的是行为面那两条钉子。

四、验证

六门(pnpm verify 的组成)全绿:

pnpm validate   ✓ Validation passed (1255ms)                 EXIT=0
pnpm typecheck  tsc --noEmit                                  EXIT=0
pnpm lint       13 warning(s), 14 suggestion(s)(均为既有)    EXIT=0
pnpm hygiene    ✓ no raw control bytes in first-party files   EXIT=0
                (content / .changeset 共 455 个文件在扫描面内)
pnpm build      ✓ Build complete (1389ms)                     EXIT=0
pnpm test       Test Files 77 passed (77)
                Tests 1824 passed | 1 skipped (1825)          EXIT=0

另跑 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' 自扫四个改动文件,无命中。changeset:.changeset/forecasting-docs-typed-period-start.md(patch)。

五、范围外发现

行为面的疑点按要求只记录不修,已另开 #1008finding,未指派):手填 period_startperiod_end 走的是「起点 + 3 个月」的滚动窗口而非所属日历季度,因此 2026-08-15 会推出 2026-10-31 却仍标 Q3 2026;并附一条未实测的二级后果(夜间 sweep 以 period_start ... today ... period_end 选行,可能把这类手工行当成本季度行覆盖)。三种修法都动 crm_forecast 写入契约,需维护者拍板,故不由本文档单顺手带。


Generated by Claude Code

The "How periods are derived" section taught both halves of the derivation
in two consecutive sentences — "Supply a `period_start` to snapshot a
specific quarter or month", then "Because the boundary is always computed,
never typed, every snapshot for the same quarter lines up exactly". Read
together they promise that any `period_start` a caller sends is snapped onto
the calendar boundary. `forecast_derive_period` fills blanks and never
rewrites a value that arrived with the record, so a mid-quarter date is
stored mid-quarter, labelled Q3 2026, and missed by every surface that pins
`period_start` to the quarter's first day.

Rewrite the claim to the measured behaviour on all three locale pages, and
pin both halves of it in test/hooks-runtime-service.test.ts so the prose
goes red with the handler if the derivation ever starts snapping.

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 6:49pm

Request Review

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

forecasting.mdx 的「boundary is always computed, never typed」措辞,对手填 period_start 的情形是过度承诺

2 participants