Skip to content

[Decision] Who presses Start Signing? Records holds the capability but cannot read an approved contract, legal can read it but lacks the capability, and clm_admin cannot write the contract #100

Open

Description

@objectstack-fleet

Ruled: 6393735625 · letter A · 2026-10-25T05:02Z

Unblocked: the 17.7.0 → 18.1.0 upgrade landed as PR #110 (7b736a0).

Filing class: ② a decision only the maintainer can make (permission scope). Reader: the maintainer; then the repo:hotclm seat dispatches the ruled option.

Source: the #87 full browser pass on 17.7.0 (main @ c31c7e2) — finding 4 in its report (the os-dev-report comment on #87); screenshots 074–086 per that report's index.

Governing text: DESIGN.md §04 (permission matrix: records RU(signing 及之后;执行与归档字段); sharing rule contract_records_execution — status in [signing, active, expired, terminated]; action gate execute_contract) · §03 state machine (approved → signing requires a current clean version) · §05 (the header carries 「发起签署」 but does not say who presses it).

What was measured

Audience Reads an approved contract? Holds execute_contract (gates Start Signing)? Result
Records Manager no — GET /api/v1/data/clm_contract/ID → 404 RECORD_NOT_FOUND; sees 0 of 5 approved contracts ("Record not found", 075) yes (src/profiles/records.profile.ts:32) cannot press it
Legal yes no no Start Signing button (076)
clm_admin yes yes (src/profiles/admin.profile.ts:42) sees the button, but PATCH clm_contract/ID → 403 "You do not have access to this record" and adding the clean version → 403 … master not editable (row-level security) (077–080): the clm_admin set carries no update RLS policy on clm_contract, so 17.5's fail-closed denies it

The contract only reached signing because legal uploaded the clean version (084/085) and then clm_admin pressed Start Signing (086). On a real install nobody can.

维护者速读

事情:「发起签署」(已批准 → 签署中)现在没有人能正常按下去。档案岗有这个按钮权限(execute_contract),但看不到已批准的合同(它的可见范围从「签署中」才开始);法务看得到合同,却没有这个权限;管理员两样都有,但写不了合同本身(没有更新的行级策略)。

你要做的:回一个字母。

做法 代价 / 收益
A(推荐) 档案岗的只读可见范围前移一格,包含 approved(编辑仍从 signing 开始,FLS 照旧) 与现有设计意图一致:execute_contract 本来就给了档案岗(执行通道),只是可见窗口晚了一格;改动最小(一条共享规则 / 读策略)
B 给法务 execute_contract 法务直接发起签署;但该权限同时门控「生效」与「补录」(见 #43),法务也就能生效和补录了,权限面变大
C 新增一个更窄的 start_signing 能力给法务,「生效」仍归档案岗 职责划分最细;但新增一个公开能力名(能力扩张从紧)

另附一个不需要你裁的缺口(席位会作为缺陷修):clm_admin 权限集缺 clm_contract 的更新行级策略,与 README「对所有 CLM 对象有完全权限」不符——其根因与 #11 第二问(谁持有 clm_requester)相关,修复会等 #11 的裁决或单独立卡。

四棱分析

  • 实际业务需求:真实且已测——按 README 配好的安装里,合同停在「已批准」,没有任何人能推进到签署。
  • 项目长远合理性:A 只是把已有设计(档案岗 = 执行通道)补完整;B 把执行权混进法务;C 增加一个能力名。
  • 防 AI 写错:A 让「谁有 execute_contract 谁就看得到要执行的合同」成为一致的声明;B 让一个权限同时管三件事,更容易被误授。
  • 创业阶段不扩散:A 最小;C 新增公开面。

Dedupe

All 54 hotclm issues (open and closed) grepped for Start Signing|start_signing|execute_contract → #43 (open — the backfill action's AND gate, a different action) and #4 (closed — the original position / set bindings); none asks who presses Start Signing.


Filed by the repo:hotclm PM seat from the #87 browser pass.

Activity

  1. zhuangjianguo commented on Oct 25, 2026

    @zhuangjianguo
    Collaborator

    Ruling: batch #1 item 3 · letter A · maintainer 「同意」 2026-10-25T05:02Z

    裁决 A:档案岗(clm_records)的只读可见范围前移一格,包含 approved;编辑窗口仍从 signing 开始,FLS 照旧。于是持有 execute_contract 的档案岗能看到并按下「发起签署」。B(给法务 execute_contract)与 C(新增 start_signing 能力)不采纳。

    工作量(卡面读数):hotclm · 1 个 PR(src/sharing/ 读窗口 + DESIGN.md §04 一行)· 量级同 PR #88(+29/−0,一个权限文件)。

    Governing text(录裁前重读,main @ e4997a8):DESIGN.md §04 档案岗 RU(signing 及之后)、共享规则 contract_records_execution、动作门控 execute_contract;§05 header「发起签署」。读数:src/profiles/records.profile.ts:32 仍只给档案岗与管理员 execute_contract;src/sharing/_lifecycle.ts:53 EXECUTION_STATUSES 仍是「签署中及之后」。前提成立。

    裁后执行:等 18.1 升级 PR 合入后派 1 张卡(18.1 把岗位绑定改到了岗位定义上,先合再动权限面):改共享规则的读窗口,同笔更新 DESIGN.md §04 那一行(受管文件,合并由维护者点击)。验收:档案岗账号在 approved 合同上看到并成功按下「发起签署」,在 approved 合同上仍不能编辑。卡上另记的 clm_admin 缺 clm_contract 更新行级策略不在本裁范围,随 #11 第二问处理。

    鲜度门:正文最后编辑 2026-10-10T03:32Z,其后无评论。决裁文档:https://claude.ai/artifact/WpsJbszPncNgj1cDt2Ucw9


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Out of the decision box — Ruling-ref: comment 6393735625 (batch #1 item 3 · letter A · maintainer 「同意」).

    • State: needs-user-decision → pm:blocked, Blocked-by: the 18.1 upgrade PR (body line 3), as the ruling prescribes.
    • One card after the upgrade: move the records read window to include approved (sharing rule), with the DESIGN.md §04 line in the same PR (governed — the maintainer clicks merge).

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Unblocked: PR #110 (the 18.1 upgrade) merged as 7b736a0. State pm:blocked → pm:queue; dispatch follows its ruling.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14
    Session: session_017nzWFRUo2U7evMkPdVniAn
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-100-records-read-approved
    Worktree: hotclm-issue-100
    Domain: repo:hotclm (single-lane sister repo — no domain:*)
    Seat: repo:hotclm#1
    File surface: src/sharing/** (the records read window) — 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 records sharing window (starts at signing, so the holder of execute_contract cannot see an approved contract) | none known | every records clerk who should press Start Signing
    Thread-read: 6397086016
    Ruling-ref: 6393735625 (batch #1 item 3 · letter A)
    Premise: re-measured by the dev on origin/main ef93d9b (18.1.0)
    Serial constraints cleared: PR #110 merged 7b736a0 (positions now name their sets). In flight: #86 (requester.profile.ts, contract.object.ts), #38 (src/views, src/pages, translations) — disjoint. Scope of this branch: the code half (Part of #100). The DESIGN.md §04 line is governed: the seat carries it in one design-doc PR with the other ruled governed lines (#96 §03, #11 §04 table, #38 §03), which the maintainer merges.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
    "issue": 100,
    "status": "done",
    "branch": "claude/issue-100-records-read-approved",
    "pr": "#113",
    "session": "session_017nzWFRUo2U7evMkPdVniAn",
    "premise_still_valid": true,
    "summary": "Ruling A, code half: added sharing rule contract_records_approved_read (position clm_records_manager, read, status in [approved]) beside the unchanged contract_records_execution (edit, signing onward). The new rule is needed because a rule carries one access level. RECORDS_READ_ONLY_STATUSES in _lifecycle.ts is derived as POST_APPROVAL minus EXECUTION, and the module load fails if it is ever anything but [approved]. EXECUTION_STATUSES still drives the edit rule and the clm_records update row policy, and records.profile.ts is untouched. The Records Manager position description is updated. Diff: 4 files under src/sharing/ only. Draft PR #113 is 'Part of #100'; the governed DESIGN.md §04 wording is proposed under its Acceptance notes for the seat to carry.",
    "tests": "Real boot on 480fe9b vs main ef93d9b: pnpm demo on a clean .objectstack/data each time, 120 contracts. Records clerk created via /api/v1/auth/admin/create-user + change-password + sys_user_position clm_records_manager; a legal account was created the same way only to upload the §03-required clean version; then POST /api/v1/sharing/rules/ID/evaluate. (a) main: records sees 0/4 approved; GET approved A answers 404 RECORD_NOT_FOUND; the browser record page shows 'Record not found' with 0 Start Signing buttons; POST actions/clm_contract/start_signing answers 404. (b) branch: 4/4 approved visible; GET A answers 200; in the browser (Playwright, /opt/pw-browsers/chromium) the header shows Start Signing and no Edit, the click sends POST /api/v1/actions/clm_contract/start_signing, which answers 200, and the header becomes Activate · Edit with the path on Signing; read-back A status=signing as admin and as records (approved 3/3, signing 7/7). (c) branch: records PATCH on approved B of {archive_no}, {status:cancelled} and {title} each answer 403 PERMISSION_DENIED 'You do not have access to this record'; admin read-back shows B unchanged; POST clm_contract_version on B answers 403 'requires edit access to its master record'. (d) draft/submitted/in_review/in_approval visible 0/10, 0/6, 0/12, 0/8 on both; GET of one each answers 404 on both. (e) signing/active/expired/terminated visible 6/6, 60/60, 8/8, 4/4 on both; PATCH archive_no on expired answers 200 on both; PATCH title on signing answers 403 FLS 'Field write denied' on both (and on A once it is signing). Boot-phase WARN lines diff empty main vs branch; seed summary identical (inserted 820, errored 404, 405 SeedLoader lines each). No ablation: no test suite pins this behaviour, and the change is metadata.",
    "gates": "Local on 480fe9b, exit codes captured to files before any pipe: pnpm install 0; pnpm validate 0 ('Validation passed'); pnpm lint 0 ('6 suggestion(s)', the pre-existing approval-approvers-may-resolve-empty set); pnpm typecheck 0; pnpm lint:i18n-gate 0 ('0 missing keys across 2 locale(s)'). CI on head 480fe9b: Validate (push run 40192325444) completed success; Validate (pull_request run 40193307189) in_progress when this report was written. dispatch-gates.mjs not applicable (sister repo; gates taken from AGENTS.md).",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "0",
    "api_writes": "3 — git push of the branch (not REST); fleet-write relay pr_create (draft, assignees zhuangjianguo): POST /repos/objectstack-ai/objectstack/dispatches executed as POST /repos/objectstack-ai/hotclm/pulls + assignees POST, giving #113; this os-dev-report comment via post-stamped (POST /repos//issues/100/comments). The first pr_create attempt was refused locally by write-pace (exit 10, budget 40/h) with zero writes, and was resent after the window reopened.",
    "open_questions": [],
    "out_of_scope_findings": [
    "observation · on the branch, the records clerk's record page logs 403 on GET /api/v1/data/sys_approval_request (record_id filter) on A, both before and after the press; whether main logs it on a signing contract is NOT MEASURED; records holds no read on that platform object and §04 grants none · carrier: none — noted in the PR Acceptance notes, not filed",
    "observation · src/profiles/records.profile.ts docblock describes only contract_records_execution as the row source; still true for edit, silent on the new read rule; left untouched under the dispatch's file-surface condition · carrier: none — noted in the PR Acceptance notes",
    "the clm_admin missing update row policy on clm_contract is out of scope by the ruling · carrier: #11"
    ]
    }

  6. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #113 · 2026-10-25T12:33Z

    reading value
    PR #113 MERGED, squash 35a77f4 on main, merged at its ACCEPT head 480fe9b
    authority maintainer, project thread, verbatim: 「你可以自己审核的就自己合并」

    Release: ruling A's code half is delivered — records reads approved contracts and can press 发起签署; it still edits only from signing on. What stays on this card: the DESIGN.md §04 line (governed), carried by the seat's design-doc PR with the other ruled governed lines; that PR closes the card. State: pm:dispatched → pm:queue, assignee cleared.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Maintainer-action: merge PR #120 — the governed design-doc PR carrying this card's remaining ruled line (code already landed: PR #113). It closes this card.

    State: pm:queue → pm:awaiting-maintainer (no dev work left).


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions