You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
hotclm's own notifications reach a zh-CN user in English — "Contract approved: …", "Renewal decision due: …", "Expired: …" — though the platform's notify template path is per-recipient localized #160
Filing class: ① user-visible defect — reach: public door, measured in a browser on main (18.1.0 at the time, pnpm demo:zh), code read on main21f2319. Reader: the hotclm lane.
Symptom
Business Requester 1 (zh-CN UI) opens the bell after their NDA is approved and reads: Contract approved: 回归测试 NDA — 已有相对方 / Your contract … has cleared every approval rung. It can now go out for signature. The email channel carries the same English. Every notification hotclm sends itself is an English literal in a notify node's inline title / message:
src/flows/legal-review-sla.flow.ts:80-81, :103-104 (30 / 60 days in review)
src/flows/turn-stalled.flow.ts:62 (waiting on the counterparty)
These are the messages a requester, a lawyer and the head of legal act on, and in a Chinese workspace they are the only English sentences left on screen.
Fix
The platform's inline title / message are "not localizable" by contract (NotifyConfigSchema). The localizable path is template + templateData: an email_template bundle with en and zh-CN members, resolved per recipient (sys_user.locale, else the deployment default) at delivery, for the inbox channel as well as email (service-messaging/src/inbox-channel.ts, #9225). Move each node above to a template; keep the {{ }} values as templateData.
If a template turns out not to render on the inbox channel in 18.2, stop and file that on the platform rather than working around it here.
Dedupe: hotclm issues (all states) grepped for notification, notify, English, template — #142 is the activity feed (platform-written, frozen at write time) and #157 is child record names; none covers the flows' own notifications.
Route: app-side. Move each notify node's inline title and message onto the platform's per-recipient template + templateData path, with en and zh-CN members, as the card describes. It shares src/flows/** with #156 (in flight), so it is dispatched after #156 lands.
Claim: PM loop round 17
Ruling-ref: none
Session: session_017nzWFRUo2U7evMkPdVniAn
Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-160-notify-templates
Worktree: hotclm-issue-160
Domain: repo:hotclm (single-lane sister repo — no domain:*)
Seat: repo:hotclm#1
File surface: the notify nodes of src/flows/{contract-approval,signature-record,renewal-notice,expiration-sweep,legal-review-sla,turn-stalled}.flow.ts · the email-template metadata they name (new files under src/ as the platform requires) · translations; stop on breach; explain in the report
Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; hand-derived for a sister repo), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: this app's own notifications (what a zh-CN recipient reads) | none expected | every requester, lawyer and head of legal who receives a CLM notification
Thread-read: 6439162948
Premise: re-measured by the dev on origin/main0159171 (@objectstack 18.2.0) with the declared demo staff (PR #171)
Serial constraints cleared: #156 landed (PR #170, flows) and #28 landed (PR #171, config / README); round 17 dispatches #43, #160 and #41 together on disjoint files; in flight #169 (lifecycle actions). #19 (seeds) follows #41; #99, #161, #162 (launch wizard) follow.
{
"issue": 160,
"status": "done",
"branch": "claude/issue-160-notify-templates",
"pr": "https://github.com/objectstack-ai/hotclm/pull/174",
"session": "session_017nzWFRUo2U7evMkPdVniAn",
"premise_still_valid": true,
"summary": "Re-measured on origin/main 0159171 (18.2.0, pnpm demo:zh, port 3152): a zh-CN requester's bell and sys_email.subject read 'Contract approved: ...' in English. The ten listed notify nodes now name an email_template bundle (template + templateData). The bundles live in src/email-templates/, one file per flow, and are registered through defineStack({ emailTemplates }). Each has an en-US member (the resolver's only fallback rung; an 'en' member would have no floor) carrying the unchanged English sentence and a zh-CN member. The platform path localizes per recipient as the card claims, on both inbox and email: measured live, including the Console's own language switch, which writes sys_user.locale.",
"tests": "Browser (Chromium /opt/pw-browsers/chromium, zh-CN context, Console login as each persona), head c4423f7. requester1 (locale zh-CN): bell '合同已批准:主服务协议 — 索伦特制造' (approval end to end through the manager, finance and executive rungs) and '合同已驳回:独立承包人协议 — 卡米尔·博尚'. counsel1 (zh-CN): '执行形式未完成:… 回签副本已收到' and '缺少执行副本:…' (signing rounds completed). requester2 (en): 'Contract rejected: …'. requester3 (unset locale, deployment default en): 'Contract rejected: …'. counsel2 (en): 'Execution formalities outstanding: … Countersigned copy returned.' Email channel: sys_email.subject matches the bell language for all 7 deliveries, status sent. 0 failed sys_notification_delivery rows. Baseline at 0159171: requester1 (zh-CN) bell 'Contract approved: 双向保密协议 — 铜田商旅' in English. NOT MEASURED in the browser: the daily sweeps (renewal decision due, expired, renewal drafted, 30/60 days in review, waiting on the counterparty). POST /api/v1/automation/{expiration_sweep,turn_stalled}/trigger answers 403 PERMISSION_DENIED ('A flow that runs on its own trigger starts there'), so no alternate route was taken. Covered instead by a render proof through plugin-email 18.2.0's EmailService.renderTemplate over createRegistryEmailTemplateLoader(allEmailTemplates), for locales zh-CN, en and fr-FR: bundles=10 members=20 failures=0. No permanent test file was added (the repo has no test suite).",
"gates": "head c4423f7, each exit captured before any pipe: pnpm install 0 · pnpm validate 0 (7 diagnostics, byte-identical to a git-archive snapshot of 0159171) · pnpm lint 0 (0 warnings, 6 suggestions, identical to 0159171) · pnpm typecheck 0 · pnpm lint:i18n-gate 0 ('0 missing keys across 2 locale(s)') · CLM_DEMO_SEED=1 pnpm validate 0. CI check-runs on PR #174: not read (in_progress at report time).",
"dogfood_battery": "n/a — hotclm, no ! in the title, Clause-②: no",
"mcp_calls": "0",
"api_writes": "2 relay writes: pr_create (objectstack-ai/hotclm#174, read back identical, assignee zhuangjianguo) and this os-dev-report comment on #160. Plus git push of the branch (not REST). Local demo DB writes only on throwaway .objectstack/data.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: inline English in src/flows/obligation-due.flow.ts (due in 7 days / due today / overdue) and src/flows/payment-overdue.flow.ts (instalment overdue): the same defect as #160, but not listed on the card and not in the claim's file surface. Read from code, not measured in a browser (both are daily sweeps, so the 403 on the trigger door applies). After PR #174 the fix is three lines each: a noticeBundle plus template/templateData on the rowBody notice. carrier: the seat (a sub-issue of #160, or a rider on its next card) · dedupe words: obligation notification English, payment overdue notify, zh-CN reminder",
"class: observation · Console language switch (account menu → 语言) writes PATCH /api/v1/data/sys_user/ID {locale:'zh-CN'|'en'}; the 'en' it writes resolves to the en-US floor member. A hotclm email_template member written as locale 'en' (matching the stack's defaultLocale) would be unreachable from en-US and dead-letter unknown tags; recorded in src/email-templates/_bundle.ts · carrier: none · noted, not filed",
"class: observation · review_started_at / turn_since render as raw ISO datetimes ('2026-09-12T00:00:00.000Z') in both languages, as the inline English already did · carrier: none · noted in PR Acceptance notes",
"class: observation · common.md says lint on main has exactly one warning (#64's security-controlled-by-parent-owner-policy-without-opt-in); measured on 0159171 it has 0 warnings and 6 suggestions · carrier: seat · noted, not filed"
],
"file_surface_note": "Beyond the notify nodes and the new src/email-templates/, this PR touches objectstack.config.ts (one emailTemplates: registration, which the platform requires), src/flows/_daily-sweep.ts (RowNotice/rowBody, the builder that emits four of the ten nodes), and signatureExecution in signature-record.flow.ts (adds missingTextZhCN from the zh-CN bundle's formality labels). RenewalStartFlow is untouched (per the seat note on #99).",
"governed_text_proposal": "none"
}
Release: hotclm's approval, signing, renewal, expiry, review-SLA and turn notifications now arrive in each recipient's language, in the bell and by email. The two reminder flows the card did not list, obligations and payments, go to a follow-up card. State: pm:dispatched removed, assignee cleared.
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage — 2026-10-27
User-visible defect, read against
main: accepted.Route: app-side. Move each
notifynode's inline title and message onto the platform's per-recipienttemplate+templateDatapath, withenandzh-CNmembers, as the card describes. It sharessrc/flows/**with #156 (in flight), so it is dispatched after #156 lands.State: →
pm:queue+bug.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 17
Ruling-ref: none
Session:
session_017nzWFRUo2U7evMkPdVniAnAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-160-notify-templatesWorktree:
hotclm-issue-160Domain:
repo:hotclm(single-lane sister repo — nodomain:*)Seat:
repo:hotclm#1File surface: the
notifynodes ofsrc/flows/{contract-approval,signature-record,renewal-notice,expiration-sweep,legal-review-sla,turn-stalled}.flow.ts· the email-template metadata they name (new files undersrc/as the platform requires) · translations; stop on breach; explain in the reportContainer & model:
S,mode:subagent,model: opus(TIER_DEFAULT; hand-derived for a sister repo),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
this app's own notifications (what a zh-CN recipient reads) | none expected | every requester, lawyer and head of legal who receives a CLM notificationThread-read: 6439162948
Premise: re-measured by the dev on
origin/main0159171(@objectstack 18.2.0) with the declared demo staff (PR #171)Serial constraints cleared: #156 landed (PR #170, flows) and #28 landed (PR #171, config / README); round 17 dispatches #43, #160 and #41 together on disjoint files; in flight #169 (lifecycle actions). #19 (seeds) follows #41; #99, #161, #162 (launch wizard) follow.
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 160, "status": "done", "branch": "claude/issue-160-notify-templates", "pr": "https://github.com/objectstack-ai/hotclm/pull/174", "session": "session_017nzWFRUo2U7evMkPdVniAn", "premise_still_valid": true, "summary": "Re-measured on origin/main 0159171 (18.2.0, pnpm demo:zh, port 3152): a zh-CN requester's bell and sys_email.subject read 'Contract approved: ...' in English. The ten listed notify nodes now name an email_template bundle (template + templateData). The bundles live in src/email-templates/, one file per flow, and are registered through defineStack({ emailTemplates }). Each has an en-US member (the resolver's only fallback rung; an 'en' member would have no floor) carrying the unchanged English sentence and a zh-CN member. The platform path localizes per recipient as the card claims, on both inbox and email: measured live, including the Console's own language switch, which writes sys_user.locale.", "tests": "Browser (Chromium /opt/pw-browsers/chromium, zh-CN context, Console login as each persona), head c4423f7. requester1 (locale zh-CN): bell '合同已批准:主服务协议 — 索伦特制造' (approval end to end through the manager, finance and executive rungs) and '合同已驳回:独立承包人协议 — 卡米尔·博尚'. counsel1 (zh-CN): '执行形式未完成:… 回签副本已收到' and '缺少执行副本:…' (signing rounds completed). requester2 (en): 'Contract rejected: …'. requester3 (unset locale, deployment default en): 'Contract rejected: …'. counsel2 (en): 'Execution formalities outstanding: … Countersigned copy returned.' Email channel: sys_email.subject matches the bell language for all 7 deliveries, status sent. 0 failed sys_notification_delivery rows. Baseline at 0159171: requester1 (zh-CN) bell 'Contract approved: 双向保密协议 — 铜田商旅' in English. NOT MEASURED in the browser: the daily sweeps (renewal decision due, expired, renewal drafted, 30/60 days in review, waiting on the counterparty). POST /api/v1/automation/{expiration_sweep,turn_stalled}/trigger answers 403 PERMISSION_DENIED ('A flow that runs on its own trigger starts there'), so no alternate route was taken. Covered instead by a render proof through plugin-email 18.2.0's EmailService.renderTemplate over createRegistryEmailTemplateLoader(allEmailTemplates), for locales zh-CN, en and fr-FR: bundles=10 members=20 failures=0. No permanent test file was added (the repo has no test suite).", "gates": "head c4423f7, each exit captured before any pipe: pnpm install 0 · pnpm validate 0 (7 diagnostics, byte-identical to a git-archive snapshot of 0159171) · pnpm lint 0 (0 warnings, 6 suggestions, identical to 0159171) · pnpm typecheck 0 · pnpm lint:i18n-gate 0 ('0 missing keys across 2 locale(s)') · CLM_DEMO_SEED=1 pnpm validate 0. CI check-runs on PR #174: not read (in_progress at report time).", "dogfood_battery": "n/a — hotclm, no ! in the title, Clause-②: no", "mcp_calls": "0", "api_writes": "2 relay writes: pr_create (objectstack-ai/hotclm#174, read back identical, assignee zhuangjianguo) and this os-dev-report comment on #160. Plus git push of the branch (not REST). Local demo DB writes only on throwaway .objectstack/data.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: inline English in src/flows/obligation-due.flow.ts (due in 7 days / due today / overdue) and src/flows/payment-overdue.flow.ts (instalment overdue): the same defect as #160, but not listed on the card and not in the claim's file surface. Read from code, not measured in a browser (both are daily sweeps, so the 403 on the trigger door applies). After PR #174 the fix is three lines each: a noticeBundle plus template/templateData on the rowBody notice. carrier: the seat (a sub-issue of #160, or a rider on its next card) · dedupe words: obligation notification English, payment overdue notify, zh-CN reminder", "class: observation · Console language switch (account menu → 语言) writes PATCH /api/v1/data/sys_user/ID {locale:'zh-CN'|'en'}; the 'en' it writes resolves to the en-US floor member. A hotclm email_template member written as locale 'en' (matching the stack's defaultLocale) would be unreachable from en-US and dead-letter unknown tags; recorded in src/email-templates/_bundle.ts · carrier: none · noted, not filed", "class: observation · review_started_at / turn_since render as raw ISO datetimes ('2026-09-12T00:00:00.000Z') in both languages, as the inline English already did · carrier: none · noted in PR Acceptance notes", "class: observation · common.md says lint on main has exactly one warning (#64's security-controlled-by-parent-owner-policy-without-opt-in); measured on 0159171 it has 0 warnings and 6 suggestions · carrier: seat · noted, not filed" ], "file_surface_note": "Beyond the notify nodes and the new src/email-templates/, this PR touches objectstack.config.ts (one emailTemplates: registration, which the platform requires), src/flows/_daily-sweep.ts (RowNotice/rowBody, the builder that emits four of the ten nodes), and signatureExecution in signature-record.flow.ts (adds missingTextZhCN from the zh-CN bundle's formality labels). RenewalStartFlow is untouched (per the seat note on #99).", "governed_text_proposal": "none" }Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Ruling-ref: none (triaged defect, comment 6439162948)
Landed — PR #174 · 2026-10-27
MERGED(squash onmain), merged at its ACCEPT headc4423f7;Fixes #160closes this cardRelease: hotclm's approval, signing, renewal, expiry, review-SLA and turn notifications now arrive in each recipient's language, in the bell and by email. The two reminder flows the card did not list, obligations and payments, go to a follow-up card. State:
pm:dispatchedremoved, assignee cleared.Generated by Claude Code