From 4d934c92bf161a01c029f0ce8d7e2232c1fcc509 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 15:44:36 +0000 Subject: [PATCH 1/3] fix(i18n): rewrite the remaining help texts for the person reading them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Descriptions in the approval flow, the datasets, the row policies, the sharing rules and the party's Country help were developer notes: DESIGN.md sections, policy names, "the daily job stamps", a 会签 step name in the English UI, and a Country help promising governing-law defaults and screening that nothing in the app performs. Each now says what the reader needs; the notes moved to code comments beside the text. Bundles mirror every inline change in en and zh-CN. The intake wizard's version step showed `templateNote`, a runtime assignment string, so zh-CN users read English. On 18.1.0 an assignment value is not translatable and a screen text slot's hole is a bare variable path, so the step now carries a static description (a bundle key) and the template sentence moved to the "Draft from template" toggle's inlineHelpText (also a bundle key), shown only on a type with a template. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn --- src/datasets/cycle-time.dataset.ts | 3 ++- src/datasets/obligation.dataset.ts | 2 +- src/datasets/payment.dataset.ts | 2 +- src/flows/contract-approval.flow.ts | 11 ++++++--- src/flows/contract-intake.flow.ts | 27 ++++++++++++++--------- src/objects/party.object.ts | 4 +++- src/profiles/admin.profile.ts | 8 +++++-- src/profiles/finance.profile.ts | 10 ++++++--- src/profiles/legal.profile.ts | 13 +++++++---- src/profiles/records.profile.ts | 4 +++- src/profiles/requester.profile.ts | 6 +++-- src/sharing/contract.sharing.ts | 20 +++++++++++------ src/translations/en/app.ts | 6 ++--- src/translations/en/flows.ts | 11 ++++----- src/translations/en/objects.catalog.ts | 2 +- src/translations/zh-CN/app.ts | 2 +- src/translations/zh-CN/flows.ts | 8 ++++--- src/translations/zh-CN/objects.catalog.ts | 2 +- 18 files changed, 91 insertions(+), 50 deletions(-) diff --git a/src/datasets/cycle-time.dataset.ts b/src/datasets/cycle-time.dataset.ts index dbb67bb..88aa7ef 100644 --- a/src/datasets/cycle-time.dataset.ts +++ b/src/datasets/cycle-time.dataset.ts @@ -66,8 +66,9 @@ import { defineDataset } from '@objectstack/spec/ui'; export const CycleTimeDataset = defineDataset({ name: 'contract_cycle_time', label: 'Contract Cycle', + // Why there is no duration measure: the module comment above. description: - 'Stage coverage by contract type and legal owner: how many contracts have reached each lifecycle stamp, and when. Carries no duration measure — see the module comment for the measurement that rules one out.', + 'Stage coverage by contract type and legal owner: how many contracts have reached each lifecycle stage, and when. It counts the stages reached; it does not measure how long a stage took.', object: 'clm_contract', include: ['contract_type'], diff --git a/src/datasets/obligation.dataset.ts b/src/datasets/obligation.dataset.ts index ed00bb3..36786f7 100644 --- a/src/datasets/obligation.dataset.ts +++ b/src/datasets/obligation.dataset.ts @@ -21,7 +21,7 @@ import { defineDataset } from '@objectstack/spec/ui'; export const ObligationDataset = defineDataset({ name: 'obligation_metrics', label: 'Obligation Metrics', - description: 'Post-signature commitments by status, kind, owner, contract and due month — including the arrears the daily job stamps.', + description: 'Post-signature commitments by status, kind, owner, contract and due month — including those the daily check has marked overdue.', object: 'clm_obligation', // `contract` is the master-detail parent; one hop to the contract, two to its diff --git a/src/datasets/payment.dataset.ts b/src/datasets/payment.dataset.ts index ff88f8c..a339561 100644 --- a/src/datasets/payment.dataset.ts +++ b/src/datasets/payment.dataset.ts @@ -29,7 +29,7 @@ export const PaymentDataset = defineDataset({ name: 'payment_metrics', label: 'Payment Metrics', description: - 'Instalments planned against actual, by month, counterparty and contract direction — plus the arrears the daily job stamps. Amounts are each contract\'s own currency and are not FX-converted.', + 'Instalments planned against actual, by month, counterparty and contract direction — including those the daily check has marked overdue. Amounts are each contract\'s own currency and are not FX-converted.', object: 'clm_payment_plan', include: ['contract', 'contract.party'], diff --git a/src/flows/contract-approval.flow.ts b/src/flows/contract-approval.flow.ts index 223e1a0..eca0433 100644 --- a/src/flows/contract-approval.flow.ts +++ b/src/flows/contract-approval.flow.ts @@ -205,7 +205,10 @@ const RUNG_POLICY = { export const ContractApprovalFlow: Flow = { name: 'contract_approval', label: 'Contract Approval Ladder', - description: 'Five-rung approval for a contract entering approval: direct manager, then the legal/finance joint sign-off, the executive and the general manager as the routing flags require.', + // "As the approval matrix requires" = the route_* flags: F2 stamps them from + // the matching clm_approval_rule rows, and F6 adds route_legal_head for an + // accepted deviation from a flagged clause (see the header). + description: 'Approval for a contract entering approval: the direct manager first, then the head of legal and/or the finance controller, the executive and the general manager, as the approval matrix and any accepted clause deviations require.', type: 'record_change', status: 'active', // See the header — the approvals service's mirror write carries no user. @@ -288,7 +291,9 @@ export const ContractApprovalFlow: Flow = { { id: 'legal_finance_joint', type: 'approval', - label: 'Head of Legal + Finance Controller (会签)', + // The step name approvers read. 会签 in the zh-CN bundle; "joint + // sign-off" here, so the English UI carries no Chinese. + label: 'Head of Legal + Finance Controller (joint sign-off)', config: { // 会签: `per_group` advances only once EACH group has reached // `minApprovals`. The two group labels are what make them two groups — @@ -441,7 +446,7 @@ export const ContractApprovalFlow: Flow = { // Rung 2 selection — four branches over two flags, in opposite polarity so // they partition: exactly one is true for every pair of values. { - id: 'e06', source: 'decision_rung2', target: 'legal_finance_joint', type: 'default', label: 'Legal + finance (会签)', + id: 'e06', source: 'decision_rung2', target: 'legal_finance_joint', type: 'default', label: 'Legal + finance, jointly', condition: when(routed('legal'), routed('finance')), }, { diff --git a/src/flows/contract-intake.flow.ts b/src/flows/contract-intake.flow.ts index 6dc96f8..2fa95b1 100644 --- a/src/flows/contract-intake.flow.ts +++ b/src/flows/contract-intake.flow.ts @@ -205,17 +205,23 @@ export const ContractIntakeFlow: Flow = { id: 'refuse_type', type: 'script', label: 'Refuse: Type Unavailable', config: { function: 'clm_intake_refuse', inputs: { kind: 'type_unavailable' } }, }, - // Both branches bind `templateAvailable` and `templateNote`, so no later - // read meets an unbound variable (the house rule of the flow-variable - // conditions table in the spec's FlowVariableSchema note). + // Both branches bind `templateAvailable`, so no later read meets an + // unbound variable (the house rule of the flow-variable conditions table + // in the spec's FlowVariableSchema note). + // + // No sentence is assigned here. An assignment value is not translatable, + // and a screen text slot's `{{ }}` hole is a variable path with no + // branching, so a note picked here rendered in English under every locale. + // The version screen below says what holds for every type, and the + // template sentence is the toggle's own help text — both bundle keys. { id: 'decision_template', type: 'decision', label: 'Type Has Template?' }, { id: 'template_yes', type: 'assignment', label: 'Template Available', - config: { assignments: { templateAvailable: true, templateNote: 'This contract type carries a template. Tick "Draft from template" to attach it as version 1, or leave it clear to upload the first version on the next step.' } }, + config: { assignments: { templateAvailable: true } }, }, { id: 'template_no', type: 'assignment', label: 'No Template', - config: { assignments: { templateAvailable: false, templateNote: 'This contract type carries no template; upload the first version on the next step.' } }, + config: { assignments: { templateAvailable: false } }, }, // The type's `intake_fields` list, normalised to an array on both branches // and carried onto the core screen as a hidden field — see the header. @@ -325,16 +331,17 @@ export const ContractIntakeFlow: Flow = { id: 'screen_document', type: 'screen', label: 'First Version', config: { title: 'First version', - description: '{{ templateNote }}', + description: 'The document this contract starts from. By default you upload it on the next step.', fields: [ // Carrier, as `intakeFields` on the core screen: `visibleWhen` sees // only this screen's own fields, so the type's answer is brought onto // the screen (server-interpolated, a boolean) and hidden by its own - // predicate. The toggle is then offered only on a type that carries - // a template; on one that does not, the description above says so - // and there is nothing to tick. + // predicate. The toggle — and the sentence about the template, its + // help text — is then offered only on a type that carries a + // template; on one that does not, the description above is the + // whole screen and there is nothing to tick. { name: 'templateAvailable', label: 'Template available', type: 'boolean', defaultValue: ref('templateAvailable'), visibleWhen: 'templateAvailable == null' }, - { name: 'draft_from_template', label: 'Draft from template', type: 'boolean', defaultValue: ref('draft_from_template'), visibleWhen: 'templateAvailable == true' }, + { name: 'draft_from_template', label: 'Draft from template', type: 'boolean', defaultValue: ref('draft_from_template'), visibleWhen: 'templateAvailable == true', inlineHelpText: 'This contract type carries a template. Tick to attach it as version 1 instead of uploading a file.' }, ], }, }, diff --git a/src/objects/party.object.ts b/src/objects/party.object.ts index 819fa53..b979d2b 100644 --- a/src/objects/party.object.ts +++ b/src/objects/party.object.ts @@ -56,7 +56,9 @@ export const Party = ObjectSchema.create({ label: 'Country', group: 'identity', maxLength: 2, - description: 'ISO 3166-1 alpha-2 country code of the party (e.g. US, DE, CN). Drives governing-law defaults and screening.', + // Nothing reads this field yet: no governing-law default and no + // screening step uses it, so the help text promises neither. + description: 'Two-letter ISO 3166-1 code of the country the party is based in (e.g. US, DE, CN).', }), registration_no: Field.text({ label: 'Registration / Tax ID', diff --git a/src/profiles/admin.profile.ts b/src/profiles/admin.profile.ts index c3f8d06..5ee44fd 100644 --- a/src/profiles/admin.profile.ts +++ b/src/profiles/admin.profile.ts @@ -85,7 +85,10 @@ export const AdminSet = definePermissionSet({ { name: 'contract_admin_edit_any_status', label: 'The administrator edits a contract in any status', - description: 'The permissive companion of contract_requester_edit_window (policies OR-merge across the sets a person holds; Modify All Data does not skip them on this object).', + // The permissive companion of contract_requester_edit_window: policies + // OR-merge across the sets a person holds, and Modify All Data does not + // skip them on this object. + description: 'Lets the CLM administrator edit a contract in any status, not only while it is Draft or Submitted.', object: 'clm_contract', operation: 'update', using: inList('status', CONTRACT_STATUSES), @@ -93,7 +96,8 @@ export const AdminSet = definePermissionSet({ { name: 'obligation_admin_update_any', label: 'The administrator updates any obligation', - description: 'The permissive companion of obligation_requester_own.', + // The permissive companion of obligation_requester_own. + description: 'Lets the CLM administrator update any obligation, not only the ones assigned to them.', object: 'clm_obligation', operation: 'update', using: inList('status', OBLIGATION_STATUSES), diff --git a/src/profiles/finance.profile.ts b/src/profiles/finance.profile.ts index 22fb0d4..0d7d645 100644 --- a/src/profiles/finance.profile.ts +++ b/src/profiles/finance.profile.ts @@ -53,7 +53,9 @@ export const FinanceSet = definePermissionSet({ { name: 'contract_finance_post_approval_window', label: 'Finance edits a contract from approved onward', - description: 'DESIGN.md §04 contract_finance_post_approval, restated as the row policy that survives the OR-merge with clm_requester.', + // DESIGN.md §04 contract_finance_post_approval, restated as the row policy + // that survives the OR-merge with clm_requester. + description: 'Lets finance edit a contract that is Approved, Signing, Active, Expired or Terminated.', object: 'clm_contract', operation: 'update', using: inList('status', POST_APPROVAL_STATUSES), @@ -61,7 +63,8 @@ export const FinanceSet = definePermissionSet({ { name: 'review_finance_stage_insert', label: 'Finance records finance-stage reviews', - description: 'DESIGN.md §04: RCU(stage=finance).', + // DESIGN.md §04: RCU(stage=finance). + description: 'Finance can record a review only at the finance stage.', object: 'clm_review', operation: 'insert', check: 'stage == "finance"', @@ -69,7 +72,8 @@ export const FinanceSet = definePermissionSet({ { name: 'review_finance_stage_update', label: 'Finance updates finance-stage reviews', - description: 'DESIGN.md §04: RCU(stage=finance).', + // DESIGN.md §04: RCU(stage=finance). + description: 'Finance can update a review only at the finance stage.', object: 'clm_review', operation: 'update', using: 'stage == "finance"', diff --git a/src/profiles/legal.profile.ts b/src/profiles/legal.profile.ts index 3be0f92..42dc07b 100644 --- a/src/profiles/legal.profile.ts +++ b/src/profiles/legal.profile.ts @@ -75,7 +75,9 @@ export const LegalSet = definePermissionSet({ { name: 'contract_legal_edit_any_status', label: 'Legal edits a contract in any status', - description: 'The permissive companion of contract_requester_edit_window (policies OR-merge across the sets a person holds).', + // The permissive companion of contract_requester_edit_window (policies + // OR-merge across the sets a person holds). + description: 'Lets legal edit a contract in any status, not only while it is Draft or Submitted.', object: 'clm_contract', operation: 'update', using: inList('status', CONTRACT_STATUSES), @@ -83,7 +85,8 @@ export const LegalSet = definePermissionSet({ { name: 'review_legal_insert_any_stage', label: 'Legal records a review at any stage', - description: 'The permissive companion of the finance stage policies on clm_review.', + // The permissive companion of the finance stage policies on clm_review. + description: 'Lets legal record a review at any stage, the finance stage included.', object: 'clm_review', operation: 'insert', check: inList('stage', REVIEW_STAGES), @@ -91,7 +94,8 @@ export const LegalSet = definePermissionSet({ { name: 'review_legal_update_any_stage', label: 'Legal updates a review at any stage', - description: 'The permissive companion of the finance stage policies on clm_review.', + // The permissive companion of the finance stage policies on clm_review. + description: 'Lets legal update a review at any stage, the finance stage included.', object: 'clm_review', operation: 'update', using: inList('stage', REVIEW_STAGES), @@ -99,7 +103,8 @@ export const LegalSet = definePermissionSet({ { name: 'obligation_legal_update_any', label: 'Legal updates any obligation of a contract it can edit', - description: 'The permissive companion of obligation_requester_own.', + // The permissive companion of obligation_requester_own. + description: 'Lets legal update any obligation of a contract it can edit, not only the ones assigned to it.', object: 'clm_obligation', operation: 'update', using: inList('status', OBLIGATION_STATUSES), diff --git a/src/profiles/records.profile.ts b/src/profiles/records.profile.ts index f83678b..84f455b 100644 --- a/src/profiles/records.profile.ts +++ b/src/profiles/records.profile.ts @@ -52,7 +52,9 @@ export const RecordsSet = definePermissionSet({ { name: 'contract_records_execution_window', label: 'Records edits a contract from signing onward', - description: 'DESIGN.md §04 contract_records_execution, restated as the row policy that survives the OR-merge with clm_requester.', + // DESIGN.md §04 contract_records_execution, restated as the row policy + // that survives the OR-merge with clm_requester. + description: 'Lets the records manager edit a contract that is Signing, Active, Expired or Terminated.', object: 'clm_contract', operation: 'update', using: inList('status', EXECUTION_STATUSES), diff --git a/src/profiles/requester.profile.ts b/src/profiles/requester.profile.ts index 8c1e59a..23345c2 100644 --- a/src/profiles/requester.profile.ts +++ b/src/profiles/requester.profile.ts @@ -138,7 +138,8 @@ export const RequesterSet = definePermissionSet({ { name: 'contract_requester_edit_window', label: 'Requesters edit a contract only while it is draft or submitted', - description: 'DESIGN.md §04: RCU(本人发起,draft/submitted 可改). From review onward the contract is edited by legal, finance and records through their own grants.', + // DESIGN.md §04: RCU(本人发起,draft/submitted 可改). + description: 'A requester can change a contract only while it is Draft or Submitted. From review onward, legal, finance and records make the changes.', object: 'clm_contract', operation: 'update', using: inList('status', REQUESTER_EDITABLE_STATUSES), @@ -146,7 +147,8 @@ export const RequesterSet = definePermissionSet({ { name: 'obligation_requester_own', label: 'Requesters update the obligations they own', - description: 'DESIGN.md §04: RU(本人负责). Reading follows the contract; updating needs the obligation to be assigned to the caller.', + // DESIGN.md §04: RU(本人负责). + description: 'A requester sees the obligations of the contracts they can see, and updates only the ones assigned to them.', object: 'clm_obligation', operation: 'update', using: 'owner == current_user.id', diff --git a/src/sharing/contract.sharing.ts b/src/sharing/contract.sharing.ts index 7f02e8a..71c7e34 100644 --- a/src/sharing/contract.sharing.ts +++ b/src/sharing/contract.sharing.ts @@ -40,7 +40,8 @@ export const ContractLegalAllSharingRules = [ defineSharingRule({ name: 'contract_legal_all_counsel', label: 'Contracts — Legal Counsel (all)', - description: 'DESIGN.md §04 contract_legal_all: legal counsel reads and edits every contract.', + // DESIGN.md §04 contract_legal_all, legal counsel's row. + description: 'Legal counsel reads and edits every contract.', object: 'clm_contract', type: 'criteria', condition: P`record.status in ${CONTRACT_STATUSES}`, @@ -50,7 +51,8 @@ export const ContractLegalAllSharingRules = [ defineSharingRule({ name: 'contract_legal_all_head', label: 'Contracts — Head of Legal (all)', - description: 'DESIGN.md §04 contract_legal_all: the head of legal reads and edits every contract.', + // DESIGN.md §04 contract_legal_all, the head of legal's row. + description: 'The head of legal reads and edits every contract.', object: 'clm_contract', type: 'criteria', condition: P`record.status in ${CONTRACT_STATUSES}`, @@ -62,7 +64,8 @@ export const ContractLegalAllSharingRules = [ export const ContractFinancePostApprovalSharingRule = defineSharingRule({ name: 'contract_finance_post_approval', label: 'Contracts — Finance (approved onward)', - description: 'DESIGN.md §04: the finance controller edits contracts from approved onward; field-level security locks the legal fields and the status (§13 Q1).', + // DESIGN.md §04; the field lock is §13 Q1. + description: 'The finance controller edits contracts from approved onward. The legal fields and the status stay locked for finance.', object: 'clm_contract', type: 'criteria', condition: P`record.status in ${POST_APPROVAL_STATUSES}`, @@ -73,7 +76,8 @@ export const ContractFinancePostApprovalSharingRule = defineSharingRule({ export const ContractRecordsExecutionSharingRule = defineSharingRule({ name: 'contract_records_execution', label: 'Contracts — Records (signing onward)', - description: 'DESIGN.md §04: the records manager edits contracts from signing onward; field-level security limits the edit to the execution and archive fields.', + // DESIGN.md §04. + description: 'The records manager edits contracts from signing onward, and only their execution and archive fields.', object: 'clm_contract', type: 'criteria', condition: P`record.status in ${EXECUTION_STATUSES}`, @@ -94,7 +98,7 @@ export const ContractRecordsExecutionSharingRule = defineSharingRule({ export const ContractRecordsApprovedReadSharingRule = defineSharingRule({ name: 'contract_records_approved_read', label: 'Contracts — Records (approved, read only)', - description: 'DESIGN.md §04, ruling A on #100: the records manager reads approved contracts so it can start signing; it edits them only from signing onward.', + description: 'The records manager reads approved contracts so it can start signing; it edits them only from signing onward.', object: 'clm_contract', type: 'criteria', condition: P`record.status in ${RECORDS_READ_ONLY_STATUSES}`, @@ -105,7 +109,8 @@ export const ContractRecordsApprovedReadSharingRule = defineSharingRule({ export const ContractExecutiveRoutedSharingRule = defineSharingRule({ name: 'contract_executive_routed', label: 'Contracts — Executive (routed)', - description: 'DESIGN.md §04: an executive reads the contracts the approval matrix routed to the executive rung.', + // DESIGN.md §04. + description: 'An executive reads the contracts the approval matrix routed to the executive rung.', object: 'clm_contract', type: 'criteria', condition: P`record.route_executive == true`, @@ -116,7 +121,8 @@ export const ContractExecutiveRoutedSharingRule = defineSharingRule({ export const ContractGmRoutedSharingRule = defineSharingRule({ name: 'contract_gm_routed', label: 'Contracts — General Manager (routed)', - description: 'DESIGN.md §04: the general manager reads the contracts the approval matrix routed to the general-manager rung.', + // DESIGN.md §04. + description: 'The general manager reads the contracts the approval matrix routed to the general-manager rung.', object: 'clm_contract', type: 'criteria', condition: P`record.route_gm == true`, diff --git a/src/translations/en/app.ts b/src/translations/en/app.ts index 6db7179..47d985a 100644 --- a/src/translations/en/app.ts +++ b/src/translations/en/app.ts @@ -285,7 +285,7 @@ export const appSurface: Pick = { }, screen_document: { title: 'First version', - // The screen reads its sentence from the run: the type's template - // decides which of two `templateNote` assignments it shows. - description: '{{ templateNote }}', + description: 'The document this contract starts from. By default you upload it on the next step.', fields: { templateAvailable: { label: 'Template available' }, - draft_from_template: { label: 'Draft from template' }, + draft_from_template: { + label: 'Draft from template', + inlineHelpText: 'This contract type carries a template. Tick to attach it as version 1 instead of uploading a file.', + }, }, }, screen_upload: { @@ -80,7 +81,7 @@ export const flowSurface: Pick = { label: 'Contract Approval Ladder', approvals: { manager_review: { label: 'Direct Manager' }, - legal_finance_joint: { label: 'Head of Legal + Finance Controller (会签)' }, + legal_finance_joint: { label: 'Head of Legal + Finance Controller (joint sign-off)' }, legal_only: { label: 'Head of Legal' }, finance_only: { label: 'Finance Controller' }, executive_signoff: { label: 'Executive' }, diff --git a/src/translations/en/objects.catalog.ts b/src/translations/en/objects.catalog.ts index 32729f0..bf7d0a0 100644 --- a/src/translations/en/objects.catalog.ts +++ b/src/translations/en/objects.catalog.ts @@ -313,7 +313,7 @@ export const catalog: Record = { }, country_code: { label: 'Country', - help: 'ISO 3166-1 alpha-2 country code of the party (e.g. US, DE, CN). Drives governing-law defaults and screening.', + help: 'Two-letter ISO 3166-1 code of the country the party is based in (e.g. US, DE, CN).', }, registration_no: { label: 'Registration / Tax ID', diff --git a/src/translations/zh-CN/app.ts b/src/translations/zh-CN/app.ts index ddec8aa..2881304 100644 --- a/src/translations/zh-CN/app.ts +++ b/src/translations/zh-CN/app.ts @@ -285,7 +285,7 @@ export const appSurface: Pick = { }, screen_document: { title: '首个版本', - // 说明文字取自运行时:合同类型有没有模板,决定显示哪一句 `templateNote`。 - description: '{{ templateNote }}', + description: '这份合同的起点文件。默认在下一步上传。', fields: { templateAvailable: { label: '有模板' }, - draft_from_template: { label: '从模板起草' }, + draft_from_template: { + label: '从模板起草', + inlineHelpText: '这类合同带有模板。勾选后,模板会作为第 1 版附上,不用再上传文件。', + }, }, }, screen_upload: { diff --git a/src/translations/zh-CN/objects.catalog.ts b/src/translations/zh-CN/objects.catalog.ts index 447ee7d..3e16b9d 100644 --- a/src/translations/zh-CN/objects.catalog.ts +++ b/src/translations/zh-CN/objects.catalog.ts @@ -313,7 +313,7 @@ export const catalog: Record = { }, country_code: { label: '国家 / 地区', - help: '相对方所在国家或地区的两位代码,例如 US、DE、CN。适用法律的默认值和名单筛查都以它为准。', + help: '相对方所在国家或地区的两位 ISO 3166-1 代码,例如 US、DE、CN。', }, registration_no: { label: '统一社会信用代码 / 税号', From 4df15021ba6cfd3f7d7b0282997f390f8091b0fe Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 15:44:36 +0000 Subject: [PATCH 2/3] docs(comments): correct four stale code comments clm.app.ts named the retired bind-position-sets.ts twice; members hold clm_requester through isDefault. signature-record.flow.ts called is_current's default false; it is true, and the flow writes false on purpose. The records.profile.ts docblock missed the approved read rule. flows/index.ts put F8 and F15 in cards 12 and 13; both are card 12. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn --- src/apps/clm.app.ts | 5 +++-- src/flows/index.ts | 3 ++- src/flows/signature-record.flow.ts | 3 ++- src/profiles/records.profile.ts | 8 +++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/apps/clm.app.ts b/src/apps/clm.app.ts index bcb79d3..80c7e20 100644 --- a/src/apps/clm.app.ts +++ b/src/apps/clm.app.ts @@ -28,7 +28,8 @@ import { App } from '@objectstack/spec/ui'; * already pruned every row the caller's capabilities do not open, so the * landing is the first surviving row, which can differ per person. * - * Every position also holds `clm_requester` (`bind-position-sets.ts`), so + * Every member, position or not, also holds `clm_requester` (the app's + * default set, `isDefault: true` in `src/profiles/requester.profile.ts`), so * 我的合同 survives for everyone. With it listed first, every audience opened * on 我发起的: the right page for a business requester, and an empty one for * a lawyer, who launches nothing. §05 gives each audience its own partition and @@ -319,7 +320,7 @@ export const ClmApp = App.create({ { id: 'nav_legal_workbench', type: 'dashboard', dashboardName: 'legal_workbench', label: 'Legal Workbench', icon: 'scale', requiredPermissions: ['clm_legal.access'] }, // NO row gate, and not by oversight. §09's 管理层 board is for the two // leadership rungs, and `clm_executive` / `clm_general_manager` hold - // no set of their own (`bind-position-sets.ts`): their capabilities + // no set of their own (`src/sharing/positions.ts`): their capabilities // are exactly a requester's, so every spelling that hides this row // from a requester hides it from leadership too. Who reads it is an // open question on #99, not a gate to invent here. diff --git a/src/flows/index.ts b/src/flows/index.ts index ae14b4d..0c69d90 100644 --- a/src/flows/index.ts +++ b/src/flows/index.ts @@ -12,7 +12,8 @@ import { ExpirationSweepFlow } from './expiration-sweep.flow.js'; import { ExecutedUploadFlow, backfillStamps, refuseBackfill } from './executed-upload.flow.js'; // F1–F16 of DESIGN.md §06; this barrel is what `defineStack({ flows })` reads. -// F8 (e-signature) and F15 (CRM hand-off) arrive with cards 12 and 13. +// F8 (e-signature) and F15 (CRM hand-off) are not built yet; both arrive with +// card 12 (docs/backlog/12-esign-and-crm-handoff.md). export { ContractIntakeFlow } from './contract-intake.flow.js'; export { ContractApprovalFlow } from './contract-approval.flow.js'; export { SignatureRecordFlow, SignatureRecordOnCreateFlow } from './signature-record.flow.js'; diff --git a/src/flows/signature-record.flow.ts b/src/flows/signature-record.flow.ts index 387a21e..c85561c 100644 --- a/src/flows/signature-record.flow.ts +++ b/src/flows/signature-record.flow.ts @@ -307,7 +307,8 @@ export const SignatureRecordFlow: Flow = { version_no: ref('execution.nextVersionNo'), kind: 'final_signed', turn: 'internal', - // `is_current` is deliberately left at its `false` default. The + // `is_current` is deliberately written `false` here, against the + // field's `true` default (contract-version.object.ts). The // negotiation flag is what `approved → signing` reads ("a current // clean version is required"), and DESIGN.md §03 keeps // `signing → approved` open for a failed signing round — moving the diff --git a/src/profiles/records.profile.ts b/src/profiles/records.profile.ts index 84f455b..b3eb738 100644 --- a/src/profiles/records.profile.ts +++ b/src/profiles/records.profile.ts @@ -10,9 +10,11 @@ import { editableOnly, hidden, inList } from './_grants.js'; * no seal keeper; execution registration and archive are one position). * * "RU(signing 及之后;执行与归档字段)" on contracts. The rows arrive through - * `contract_records_execution` (`edit`, signing onward); the row policy - * repeats the window for the OR-merge; and field-level security limits the - * edit to the two execution/archive fields a person actually writes: + * two sharing rules: `contract_records_execution` (`edit`, signing onward) and + * `contract_records_approved_read` (`read`, approved only — ruling A on #100, + * so records can read the contract it starts signing). The row policy repeats + * the edit window for the OR-merge, and field-level security limits the edit + * to the two execution/archive fields a person actually writes: * * - `status` — execution IS the `signing → active` transition the * `execute_contract` gate authorises (the state machine guards it); From aacd2160abc61e29d4d8b7631c34998dc0593096 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 15:44:36 +0000 Subject: [PATCH 3/3] docs(readme): pnpm demo loads 830 rows The three signing companies and seven departments are seeded rows too (measured: inserted 830 on a clean database). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 73cda4f..5ebb0bb 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,10 @@ pnpm lint:i18n-gate # every zh-CN key present — a missing one renders as Engl ## The demo dataset -`pnpm demo` loads 820 rows: nine contract types, a thirty-clause playbook, a -six-rule approval matrix, forty counterparties and 120 contracts with their -reviews, deviations, signature rounds, obligations and payment instalments. +`pnpm demo` loads 830 rows: nine contract types, a thirty-clause playbook, a +six-rule approval matrix, three signing companies and seven departments, forty +counterparties and 120 contracts with their reviews, deviations, signature +rounds, obligations and payment instalments. Every date is relative to boot, so ten contracts always expire within the next thirty days and the arrears lists are always in arrears.