Repository navigation
The finance approver can open the contract it is asked to approve - #153
Merged
Merged
Conversation
The finance controller co-signs at in_approval whenever the matrix stamps route_finance (F5), yet it reached contracts only from approved onward, so the approval request it acts on read "无法打开此记录". Add the contract_finance_routed criteria rule (route_finance == true, read, the clm_finance_controller position), the same shape as the executive and general-manager routed rules. The edit window is unchanged. README: the finance position's row says what it reads, and the rung-1 section records that on the open edition the direct manager decides from the request alone until objectstack-ai/objectstack#26180 ships. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn
Contributor
Author
席位核对 — ACCEPT · head
|
| check | reading |
|---|---|
| shape | draft → main, Part of #134; mergeable clean |
| CI on the head | both Validate check-runs success on 0843d78 = PR head.sha |
| scope | 2 files: src/sharing/contract.sharing.ts (+25/−1, criteria rule contract_finance_routed, added to ClmSharingRules) and README.md (+14/−2, finance row and the rung-1 open-edition limitation). Within the claim's surface. Nothing governed |
| the ruling | 6422705361 letter A, finance half — delivered: clm_contract where route_finance == true is read for position clm_finance_controller. Measured on the dev server with a real finance persona: before, 「无法打开此记录」 on the request and API 404; after, the routed contract opens read-only |
| seat call Q1 | A — keep the executive/GM shape with no status window. F2 re-stamps all four route_* flags on every submission, so finance reads a routed contract from submitted on, the same as the other rungs. Editing still starts at approved (§13 Q1) |
| seat call Q2 | A — keep the outcome-shaped title |
| observation | src/sharing/index.ts does not re-export the individual rule constants; the rule ships through ClmSharingRules, as its siblings do. No card |
| not in this PR | rung 1 (the direct manager) stays open on #134, Blocked-by objectstack-ai/objectstack#26180 |
Landing: under the maintainer's merge authorization (project thread: 「你可以自己审核的就自己合并」).
Generated by Claude Code
zhuangjianguo
marked this pull request as ready for review
October 26, 2026 17:54
zhuangjianguo
pushed a commit
that referenced
this pull request
Oct 26, 2026
…ce half, PR #153) Ruling 6422705361 (A). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn
This was referenced Oct 26, 2026
Draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #134
The finance half (ruling 6422705361, letter A). Rung 1 (the direct manager) stays open on the card, blocked by objectstack-ai/objectstack#26180; this PR adds only its README known-limitation line.
What changed
src/sharing/contract.sharing.ts: new criteria rulecontract_finance_routedonclm_contract, conditionrecord.route_finance == true,accessLevel: 'read',sharedWith: { type: 'position', value: CLM_POSITION.financeController }(clm_finance_controller). It mirrorscontract_executive_routed/contract_gm_routedfield for field: the same recipient kind (position), the same access level, and no status window.README.md: the finance position row now says it reads every contract withroute_finance, like the executive and GM rows. The rung-1 section gets the known limitation: on the open edition the direct manager decides from the request alone until plugin-approvals: the approver a step waits on gets no read of the record it is about — "无法打开此记录" on the request they must decide objectstack#26180 ships.Why no status window. The executive and GM routed rules have none. The
route_*flags are stamped at submission and re-evaluated on every resubmission (F2contract_routewrites all four, so a flag that stops matching is cleared). A status window would also have to be kept in step with the approval ladder, which the other two rules do not do. Finance's edit window does not change: it is stillcontract_finance_post_approvalplus theclm_financeupdate row policy, both fromapprovedon. One effect follows directly: finance now reads a routed contract fromsubmitted/in_reviewon, before its rung opens. The executive and GM already read their routed contracts that way (measured below: 5submitted, 8in_reviewand 3in_approvalrouted contracts became readable).Measured (18.1.0,
pnpm demoon a throwaway DB, port 3134, Chromium/opt/pw-browsers/chromium, locale zh-CN)The personas were set up the README way:
POST /api/v1/auth/admin/create-userfor Business Requester 2, Executive 1 and Finance Controller 1;sys_user_positionrows forclm_executiveandclm_finance_controller;set-user-managerto make Business Requester 2 report to Executive 1; then each account's ownchange-password, becausemustChangePasswordis set. Business Requester 2 launched both orders through the 发起合同 wizard: type Order Form, counterparty picked, a one-page PDF uploaded as v1, 立即提交. Executive 1 approved rung 1 from 待我处理.eace613)0843d78)in_approval,route_finance: true/clm_contract/record/ID) with 通过 / 拒绝 / 转签 / 退回修改 / 退回补充 / 回复 and the badge 「审批中已锁定」/_console/apps/clm/clm_contract/record/IDGET /api/v1/data/clm_contract/IDas finance404 RECORD_NOT_FOUND200v1 · Draft, fileorder-250k.pdf; the link opens the PDF in a new tab (Chromium PDF viewer renders the page);GET /api/v1/storage/files/ID→200 application/pdf, 612 bytes,%PDF-1.4GET clm_review/clm_deviation→ 200in_approvalPATCH payment_terms→403 PERMISSION_DENIED「您无权访问这条记录…」in_reviewcontract (not approval-locked; seeded MSA-2026-0006)PATCH payment_terms→403 PERMISSION_DENIED(read-only share)route_finance: false,in_approvalat rung 1) as finance404/ 「未找到记录」404/ 「未找到记录」; its version fileGET /api/v1/storage/files/ID→403approvedor latersubmitted5 ·in_review8 ·in_approval3)approvedcontract)PATCH payment_terms200 ·PATCH governing_law403Field write deniedapproved; thenPATCH payment_terms200,PATCH status403Field write deniedRung 1 before the change (unchanged by this PR): Executive 1 opened the request and the 记录 ID card read 「无法打开此记录」 — the limitation the README line records.
The boot banner (both runs) shows no
degraded/no such table. The 404ERROR [SeedLoader]lines are the expected first-boot unresolved persona names that the demo's own note explains (inserted: 830). Browser console on the finance pages: one 404 resource load and three[object-ui]"predicate failed to evaluate" warnings for header actions. Both runs show the same lines; they are not changed here.Gates (head
0843d78, all exit 0)The built artifact carries the rule:
dist/objectstack.jsoncontains"contract_finance_routed".Proposed DESIGN.md §04 row (for the seat's governed PR #120; not touched here)
|
contract_finance_routed| clm_contract |route_finance == true| positionclm_finance_controller— read(审批中只读;编辑仍自approved起,§13 Q1) |Acceptance notes
clm_contract_version/clm_review/clm_deviationarecontrolled_by_parent, andclm_financealready has object read on them. Through this rule the finance approver sees the versions (and the version PDF), reviews, deviations and approval records. The storage door follows the record: the file of a contract finance cannot read answers 403.src/sharing/index.tsre-exports each rule by name. The newContractFinanceRoutedSharingRuleis not added there, because that file is outside the claimed file surface. It reaches the stack throughClmSharingRules, which is whatobjectstack.config.tsreads. Carrier: none — the seat can add the one export line if it wants the barrel complete.🤖 Generated with Claude Code
https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn