Repository navigation
DESIGN.md §04 gives clm_requester update on its own obligations, and controlled_by_parent refuses it for all 200 — 我负责的履约 is read-only for the audience it is built for - #165
Merged
Conversation
The ruling on #64 (letter D) waited for the platform to let a controlled_by_parent detail's owner update its row with read on the master. 18.2.0 ships it as sharing.controlledByParent.ownerMayEdit, and 18.2's lint already warns that obligation_requester_own can never admit an owner without it. This declares the opt-in, which clears that warning. The platform's owner is the injected owner_id, not the obligation's own owner field that My Obligations, F10 and obligation_requester_own key on. All 200 demo obligations carry owner_id = the dev admin, so measured on 18.2.0 the opt-in admits no requester yet; the comment says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn
Contributor
Author
席位核对 — ACCEPT (Part of #64) · head
|
| check | reading |
|---|---|
| shape | draft → main, Part of #64; mergeable clean |
| CI on the head | both Validate check-runs success on 7f5b38c = PR head.sha |
| scope | 1 file: sharing: { controlledByParent: { ownerMayEdit: true } } on clm_obligation, with a comment that records the owner / owner_id gap. This is ruled step 2 of 6395825992 (D). It is within the claim's surface. Nothing governed. Lint drops from 1 warning to 0 |
| behaviour | unchanged, as measured: Business Requester 3 is still refused (403 master edit) before and after, legal is unchanged, and the state machine still holds. The opt-in is inert today, because the 18.2.0 gate compares owner_id and hotclm's accountable person is owner. Merged anyway: every route forward needs the opt-in, and the rollback is deleting one key |
| seat calls on the open questions | Q1 → platform first (option A). Filed as objectstack-ai/objectstack#26591: the opt-in should be able to name the owner column, or lint and gate must agree on owner_id. #64 goes pm:blocked on it. If the platform rules "owner_id only", moving hotclm's accountable column becomes a decision-box item (option B; C stays out under the platform-gap rule). Q2 → the contract / owner / due_date read-only locks ride the change that makes the leg reachable, so they can be measured; this is recorded on #64. Q3 → hold the §04 sentence until requesters can actually update (option B) |
Landing: under the maintainer's merge authorization (project thread: 「你可以自己审核的就自己合并」).
Generated by Claude Code
zhuangjianguo
marked this pull request as ready for review
October 27, 2026 09:22
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 #64
Ruling-ref: 6395825992 (letter D) · post-release execution step 2, the
clm_obligationhalf. #64 stays open; see "Why this is Part of, not Fixes".What changed
src/objects/obligation.object.tsdeclares the 18.2.0 opt-in (objectstack-ai/objectstack#25760, commit5e113b3):The comment above it says what the opt-in opens (a by-id update by the row's platform owner with read on the contract; create, delete and re-pointing to another contract still need edit on the contract), and what it does not do here yet: it keys on the injected
owner_id, not on the obligation's ownownerfield.One file, one key, plus its comment.
requester.profile.tsis untouched.Why this is Part of, not Fixes: the opt-in admits no requester on 18.2.0
The platform's leg (
assertControlledByParentWrite, ADR-0055 §3 amendment) admits a caller whoseuserIdequals the stored row'sowner_id. HotCLM's accountable person is a different column:clm_obligation.owner(Field.user). That column is what 我负责的履约 filters on (my_obligations:owner == {current_user_id}), what F10 reminds, and whatobligation_requester_ownchecks (owner == current_user.id). Nothing writes the person intoowner_id:owneronly. On the measured DB, all 200 obligations haveowner_id= Dev Admin: 50, 53 and 40 of them haveowner= Business Requester 1, 2 and 3, 29 and 27 haveowner= Legal Counsel 1 and 2, and 1 has noowner.contract_activate) inserts its renewal obligation withrunAs: 'system', soowner_idis not stamped from the requester either. This is read from the code, not measured.So the condition
owner_id == callernever holds for the requester the obligation is assigned to. This was measured, and is below. Which way to line the two up is a decision, and it is in the hand-back on #64.Measurement: README personas,
pnpm demo:zh, port 3143, throwaway DB, zh-CN, @objectstack 18.2.0The personas were set up the README way:
create-userwithmustChangePasswordand thenchange-passwordas the user for Business Requester 1–3, Legal Counsel 1–2 and a Line Manager;sys_user_positionclm_legal_counselfor both counsel; Set Manager for the three requesters; thenpnpm demo:zhagain to hand the rows over. Every reading below was taken signed in as the persona named.Before =
origin/main21f2319. After = this branch.owner= me) under MSA-2026-0015 (active,owner_id= me)PATCH {status: in_progress}requires edit access to its master recordPATCH {notes}PATCH {status: done}PATCHan obligation on my own contract whoseowneris Legal Counsel 1You do not have access to this recordPATCHthe parent contract (jurisdiction)You do not have access to this recorddone → pending,pending → overdueowner, writeowner_id, re-pointcontractto another active contract or to my own draftPOST /security/explain {clm_obligation, update}allowed: trueallowed: truePATCH {status: in_progress}in_progress → overdueINVALID_STATE(only the daily job marks overdue)Browser (Chromium
/opt/pw-browsers/chromium, zh-CN): 我的合同 › 我负责的履约 lists 18 rows for Business Requester 3, before and after. I opened 提交用量报表. The record drawer shows 负责人 = Business Requester 3 and 所有者 = Dev Admin. It offers no 编辑 button, and its 更多操作 menu holds only 添加到收藏 and 分享. This was the same before and after, and no write was sent. Control: Legal Counsel 2 opened 提供反商业贿赂合规声明, clicked 编辑, set 状态 = 进行中, filled 备注 and clicked 更新. The page sentPATCH /api/v1/data/clm_obligation/…and got200, with the toast 履约义务“提供反商业贿赂合规声明”已更新. This was the same before and after. The console logged twoFailed to load resourcelines (one 404, one 401) on the login page, the same both times.Not measured: a positive reading of the opt-in, meaning a requester row whose
owner_idis the requester. The dev admin'sPATCH {owner_id}on an obligation answered403 You do not have access to this record, because the dev admin holds onlyclm_requester. Under the rules of this round, I took no other route.Gates (head
7f5b38c)Reverse leg for the lint reading: with this file temporarily restored to
origin/main,pnpm lintprinted1 warning(s), 6 suggestion(s). The warning wassecurity-controlled-by-parent-owner-policy-without-opt-in at permissions[4].rowLevelSecurity[1].using. The file was then restored withgit checkout HEAD --, and its blob hash matches HEAD2ea8e17.Acceptance notes
FIELD == current_user.idupdate policy and prescribes the opt-in. The gate the opt-in turns on compares onlyowner_id.obligation_requester_ownkeys onowner, so the lint's fix does not change what the runtime admits here. This is reported to the seat as a platform finding.owner_id=owner= them), these were read from the platform code and ADR-0055's amendment, not measured:owner_iditself stays locked, because reassigning it needsallowTransfer, whichclm_requesterdoes not hold.owner,due_date,titleandkindwould be writable, because no FLS onclm_requestercoversclm_obligation.contractcould be re-pointed onto the requester's owndraftorsubmittedcontract, because the leg asks for edit on the master the body names, and the requester has that.clm_requesterupdate on its own obligations, andcontrolled_by_parentrefuses it for all 200 — 我负责的履约 is read-only for the audience it is built for #64. No FLS was added: no requester reaches the leg today, so a lock could not be measured.🤖 Generated with Claude Code
https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn