Skip to content

fix: the zh-CN Activity group and the forecast period fields get labels of their own; approval names measured upstream (#2060 item 13) - #2199

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-2060-item-13-locale-labels
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-2060-item-13-locale-labels

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #2060 (item 13)
Clause-②: no

Item 13 of the list card (locales), under claim 6289299906, starting from main 39dee2be on @objectstack/* 18.0.0-next.5. #2060 remains open. This PR delivers two of item 13's three rows: C-16 (the zh-CN Activity group) and the forecast fold from 6287935636 (two fields labelled "Period"). The third row, C-15 (approval names), cannot be delivered in this app. The platform's translation contract reaches neither name on any approval surface, so that half goes upstream. Nothing for C-15 is written here: no pack key, and no localized label in a flow source.

The approval flows this app ships

These are all the flows under src/ that carry an approval node (grep "type: 'approval'" over src/). Every one is registered in appFlows.

Flow (name) Flow label Approval steps (node id: label)
account_approval Account Approval account_review: Account Review
lead_conversion_approval Lead Conversion Approval conversion_review: Lead Conversion Review
opportunity_approval Large Deal Approval manager_review: Sales Manager Review · director_signoff: Sales Director Sign-off
opportunity_approval_on_create Large Deal Approval (on create) the same two nodes (insert-time twin)
opportunity_qualification_approval Opportunity Qualification Approval qualification_review: Qualification Review
opportunity_status_change_approval Opportunity Status Change Approval status_review: Status Change Review

C-15: the translation contract reaches neither name

The contract (TranslationItemSchema.flows, @objectstack/spec/system) has a key for a flow's label. For a node, it has screens.NODE (screen nodes only) and refusals.NODE (refused end nodes only). No key names an approval node. Two probes, each held on disk with ablation-replace.mjs --hold and restored afterwards (blob equals HEAD, git diff HEAD empty):

Probe Reading
Flow name. zh-CN gets flows.account_approval.label and flows.opportunity_approval_on_create.label (marker text) pnpm build exits 0, and the served GET /api/v1/i18n/translations/zh-CN carries both keys. Two requests opened after that store process_label "Account Approval" and "Large Deal Approval (on create)". GET /api/v1/approvals/requests with Accept-Language: zh-CN answers those English labels. The zh-CN inbox and the record's approval panel show them too. The marker text appears 0 times on any of these surfaces.
Step name. zh-CN gets flows.opportunity_approval.screens.manager_review.title pnpm validate exits 1 with translation-target-unknown: "Translations are keyed to screen "manager_review", which flow "opportunity_approval" declares as a approval node, not a screen, so nothing resolves these keys."

Why, read in the pinned packages. @objectstack/plugin-approvals writes process_label and current_step_label when a request opens. Each is a copy of the authored Flow.label and node.label ("engine-written snapshots … a list reads them as stored and never re-derives a label from flow metadata"). The approval history's step_label is copied the same way. The console inbox draws process_label, falling back to the humanized process_name, and step_label, falling back to the humanized current_step. Neither consults the bundle. @objectstack/cli's i18n extractor asks for flows.FLOW.label only on a flow with a screen node. In its own words: "A flow that can never pause there (record-triggered, scheduled, an API flow with no screen) has no surface that shows its translated label".

Surfaces measured in headless Chromium at 1440×900 as the dev admin, on two requests opened through REST (crm_account insert and a 150,000 crm_opportunity insert). The table holds the same reading before and after this PR, in zh-CN, ja-JP and es-ES:

Surface What it shows
Approvals inbox (/component/approvals/inbox) "Large Deal Approval (on create)" over "Sales Manager Review", and "Account Approval" over "Account Review". The page chrome around them is localized (审批中心 · 承認センター · Centro de aprobaciones).
The record's approval panel "Large Deal Approval (on create) · Sales Manager Review", with steps "1 Sales Manager Review · 2 Sales Director Sign-off"
The record's approval history "提交 · Dev Admin · Manager Review": the humanized node id, not even the step's label (see the report)
The notification (bell) "Approval requested / A decision on Opportunity ID is waiting on you.", in English in all three locales. It names neither the flow nor the step. The bell's approvals tab shows only a count.

So the app's half of C-15 is empty until the platform carries a reader, plus a key for the step. The report asks the seat to file the upstream card. When it lands, os lint's i18n/missing-flow is the platform's signal for which keys the packs owe. The extractor states that its predicate "widens in the same change as that reader". Not done here, on purpose: pre-writing flows.FLOW.label for the six flows now. Those keys parse and are served, but nothing reads them. A key that is declared and not honoured is the pattern the rulings refuse.

C-16: the zh-CN Activity group gets its own word

Locale Sidebar before Sidebar after Events page heading
zh-CN 活动 › 活动 · 销售活动 互动 › 活动 · 销售活动 活动
en Activity › Events · Sales Activity unchanged Events
ja-JP 活動 › イベント · 営業活動 unchanged イベント
es-ES Actividad › Eventos · Actividad de Ventas unchanged Eventos

Why 互动. In every pack, the item's label is crm_event's plural label, which is also the heading of the page it opens. In zh-CN, that word is 活动 (the object, its views 全部活动 / 活动日历, its sections and fields). So the item keeps 活动 and the group changes. The other packs name the group by the activity it gathers. zh-CN already has a word for that: 互动 is crm_event's own description ("与客户的会议、通话及其他已安排的互动"), the 互动历史 view, and the Sales Activity tiles (每周互动数, 已记录互动, 商机上的互动). The reason sits beside the key in zh-CN/app.ts.

The docs: content/docs/sales/meetings-and-calls.zh-Hans.mdx cited the sidebar path as 活动 › 活动 and now cites 互动 › 活动. The en and zh-Hant pages cite Activity › Events (zh-Hant keeps English per the repo rule) and are unchanged. No other page under content/docs/ quotes the zh-CN group (uncapped grep, all three locales).

The forecast fold: the two period fields read apart

New Forecast form, field labels in order (Owner, Period, Period Start, Period End, then the free-text field):

Locale period period_label before period_label after
en Period* Period Period Label (the field's declared label)
es-ES Periodo* Periodo Etiqueta del periodo (as Inicio / Fin del periodo)
ja-JP 期間* 期間 期間ラベル (as 期間開始 / 期間終了, and the field's own help text)
zh-CN 周期* 周期标签 unchanged

The period_end help texts name the picklist ("Period", "Periodo", 「期間」), which keeps its label, so they still read right.

Tests

  • test/locale-label-collisions.test.ts (new, 14 cases). In every shipped pack, the Activity group shares its label with none of its items, and the Events item reads as crm_event's plural label. The forecast form's period and period_label differ in every pack, and en reads both as declared. Only these pairs are asserted; a general "no two labels collide" rule would be a lint, and lint is the platform's.
  • Ablation 1: zh-CN group_activity set back to 活动 (ablation-replace.mjs: anchor 1 → 0, blob cbc232d3 → 25591599). Result: zh-CN: the Activity group shares its label with none of its items failed with "the group and these items both read "活动": expected [ 'nav_event' ] to deeply equal []". 1 failed, 37 passed. Restored (blob equals HEAD). Observed direction: I predicted docs-setup-navigation-names.test.ts would also turn red, because the zh-Hans page then cites a group that no pack ships. It stayed green: that guard's group matcher is generated from the shipped labels, so a renamed group drops out of its sight (see Acceptance notes). The new pin is the only thing holding C-16.
  • Ablation 2: en period_label set back to "Period" (blob 9df36971 → adabbad8). Two cases failed: "expected 'Period' not to be 'Period'" and "expected 'Period' to be 'Period Label'". 2 failed, 12 passed. Restored (blob equals HEAD).
  • pnpm verify on 992ac772, heavy steps through the verify lock. validate 0 ("✓ Validation passed"). typecheck 0. lint 0. lint:i18n-gate 0 ("0 i18n/missing-* issues"). hygiene 0. hygiene:tokens 0. build 0. vitest run --shard=k/3 --maxWorkers=2: shard 1 is 69 files and 1135 tests passed; shard 2 is 68 files and 1278 passed, 1 skipped; shard 3 is 68 files and 1619 passed. That is all 205 test files. pnpm changeset status --since=origin/main: hotcrm patch.
  • Left to CI: Docs App (one content/docs line), Link Check, E2E, CodeQL.

Acceptance notes

  • test/docs-setup-navigation-names.test.ts, rule 3, cannot see a citation whose group was renamed, because its matcher is built from shipped group labels. Ablation 1 measured it: a zh-Hans page citing 活动 › 活动 after the group became 互动 stays green. This is an observation about this repo's guard. It is not filed.
  • content/docs/sales/forecasting.mdx:16 lists the record's sections as Period / Numbers / Derived / Source / Narrative. The form's sections are Snapshot / Amounts / Source (forecast.object.ts fieldGroups). This is item 16's family (docs that describe the app as it was) and is left to that item.
  • The platform findings met on the way (the approval history's step name, the notification's English, the inbox's English object name) are in the report for the seat.

Generated by Claude Code

…t labels of their own

zh-CN labelled the Activity navigation group and its Events item both
活动. The item keeps 活动, crm_event's own label and the heading of the
page it opens; the group becomes 互动, the word the pack already uses
for the interactions it gathers. The zh-Hans meetings page quotes the
sidebar path and follows.

en, es-ES and ja-JP labelled crm_forecast.period_label the same as the
period picklist beside it. It now reads Period Label / Etiqueta del
periodo / 期間ラベル; zh-CN already kept them apart.

A pin holds both pairs apart in every pack. Approval flow and step names
are not translated here: the platform's translation contract reaches
neither on any approval surface.

Claude-Session: https://claude.ai/code/session_01KqkTQNLcxVc588eZpJmp1r
Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 20, 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 20, 2026 11:44am 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 20, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT (#2060 item 13) · PR #2199 at 992ac772, brought onto main f2af4697 as ac1b2630 · repo:hotcrm seat 2 · os-support-ai · session session_01KqkTQNLcxVc588eZpJmp1r · 2026-10-20T11:46Z · report 6290063475 on hotcrm#2060

  1. Shape:
  2. The diff, read:
    • C-16: the zh-CN Activity group becomes 互动, and its Events item keeps 活动.
      • 活动 is crm_event's own label, the heading of the page the item opens.
      • 互动 is the word the pack already uses for what the group gathers: crm_event's description, the 互动历史 view and the Sales Activity tiles.
      • The other packs already used two words. The zh-Hans meetings page now cites 互动 › 活动.
    • Forecast fold: period_label reads Period Label / Etiqueta del periodo / 期間ラベル, matching the field's declared label. zh-CN was already distinct.
    • C-15, measured as unreachable: no approval surface reads a translated approval name. The request snapshots Flow.label / node.label at open, and the translation contract has no key for an approval node: screens.NODE on one is refused by translation-target-unknown. Nothing is written for it, as the ruling required. Filed upstream as Approvals show their flow and step names in the authoring language only: the request snapshots Flow.label / node.label at open, and the translation contract has no key for an approval step objectstack#24863.
    • The changeset's third bullet tells users plainly that approval names stay English and why.
  3. The open question, the seat's call: A, as the PR does. Pre-writing six flow labels that parse, are served and change nothing would be a declared-but-not-honoured surface. HotCRM writes them in the same change that a platform reader honours them.
  4. Gates:
    • The dev's verify on 992ac772:
      • the static chain: each step exit 0, with 0 i18n/missing-* issues;
      • the three shards: 1135, 1278 + 1 skipped, and 1619 passed;
      • 205 files.
    • Two ablations went red on the predicted cases, and each restore is proven blob-equal.
    • Noted: the docs-navigation guard stayed green when the zh-Hans page cited a group that no pack ships. Its rule-3 matcher is generated from the shipped group labels. Recorded in the PR's Acceptance notes.
    • CI on ac1b2630 at this review: running. Landing waits for every check.
  5. Out of scope, dispositions:

Landing: once every check on the head is green, this seat flips the PR ready and arms auto-merge, bringing main in first if it has moved. After the merge, #2060 goes back to pm:queue, with C-15 waiting on objectstack-ai/objectstack#24863.

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

Development

Successfully merging this pull request may close these issues.

2 participants