Repository navigation
18.3 lint: clm_admin's modifyAllRecords on clm_contract makes its 17 field edit locks inert — the locks are declared but bind no admin - #204
Merged
Conversation
clm_admin held modifyAllRecords on clm_contract. Since ADR-0066's 2026-11-02 note a super-user set votes editable on every field of its object, so the 17 stamped-field locks (routing flags, stage timestamps, AI fields) bound no administrator; 18.3 lints it as security-superuser-field-mask-inert. Per the ruling on #196 (letter A, the locks win), the contract entry now grants viewAllRecords + writeScope 'org' + allowTransfer instead: every row readable, every row editable and deletable, owner reassign, and no edit vote on the locked fields. The six child entries keep their super-user bits; they lock nothing. The header comment now says a lock binds only without modifyAll and whole-row write comes from writeScope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn
Contributor
Author
席位核对 — ACCEPT · head
|
| check | reading |
|---|---|
| ruling | batch 12 item 2, letter A (6617227919): the locks win; clm_admin drops modifyAllRecords on clm_contract for viewAllRecords + writeScope: 'org' + allowTransfer |
| shape | draft → main, Fixes #196; base b38df5a, mergeable clean |
| CI on the head | both Validate check-runs success on c50654e = PR head.sha |
| scope | 1 file, src/profiles/admin.profile.ts: the clm_contract entry exactly as ruled, readOnly(Contract, CONTRACT_STAMPED_FIELDS) kept, the six child entries unchanged, the header comment rewritten for ADR-0066's 2026-11-02 note. Lint 18 → 1 warning (the remaining one is #64's) |
| measured (18.3.0, as clm.admin on another user's contract) | read 200; ordinary field 200; approved_at / route_finance 403 「Field write denied」 (before: 200 with the value silently unchanged); owner_id reassign 200; child write 200; delete 200; browser edit as the admin saves |
| residuals | none for this card. The contract_admin_edit_any_status inline comment still names Modify All; it stays true and is left for the next card that touches the file |
Landing: under the maintainer's ruling (batch 12, 「同意」 2026-11-03T22:34Z) and merge authorization (project thread: 「你可以自己审核的就自己合并」).
Generated by Claude Code
zhuangjianguo
marked this pull request as ready for review
November 3, 2026 23:09
This was referenced Nov 3, 2026
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.
Fixes #196
Ruling: 6617227919 (letter A, the locks win). Claim: Ruling-ref 6617227919, branch
claude/issue-196-admin-field-locks, sessionsession_017nzWFRUo2U7evMkPdVniAn.What changed
One file,
src/profiles/admin.profile.ts. Only theclm_contractentry and the header comment changed.clm_contractis now{ allowCreate, allowRead, allowEdit, allowDelete: true, viewAllRecords: true, writeScope: 'org', allowTransfer: true }.modifyAllRecordsis gone.readOnly(Contract, CONTRACT_STAMPED_FIELDS)stays.clm_contract_version,clm_review,clm_deviation,clm_signature,clm_obligation,clm_payment_plan) still usefull. Their fields are not touched.modifyAllRecords;viewAllRecordsvotes readable and never votes an edit;writeScope: 'org';allowTransfer;writeScopesemantics, read inpackages/spec/src/security/permission.zod.tsat@objectstack/spec@18.3.0:writeScopeis the "Write (edit/delete) access DEPTH".viewAllRecords: trueit is "READ AND HONOURED. The write short-circuit does not nameviewAllRecords, so this pair stays accepted". Only the pairing withmodifyAllRecordsis refused.readScope: 'org'+writeScope: 'org') was not needed./api/v1/auth/me/permissionsfor clm.admin after the change:objects.clm_contract:allowEdit/allowDelete/allowTransfer/viewAllRecords: true,modifyAllRecords: false,writeScope: 'org'fields.clm_contract.approved_at:{ readable: true, editable: false }owner_idandjurisdiction:{ readable: true, editable: true }Readings (18.3.0,
pnpm demo:zhon a throwaway DB, port 3169, signed in as clm.admin@example.com)Every reading is on a contract another user owns. Script: sign in through
/api/v1/auth/sign-in/email, then REST calls under/api/v1/data.jurisdiction(ordinary field)approved_at[Security] Field write denied: not permitted to edit [approved_at] on 'clm_contract'·PERMISSION_DENIEDroute_finance(flip)[Security] Field write denied: not permitted to edit [route_finance] on 'clm_contract'·PERMISSION_DENIEDowner_idto a third user (reassign)clm_review.commentson that contractpnpm lint:security-superuser-field-mask-inertobligation_requester_own, #64's)Before the change, a PATCH of a stamped field answered 200 and the stored value did not move. The write was dropped silently, so on 18.3 the "overwrite" the issue describes did not land through REST. The lock now refuses the write out loud.
Admin screen flow (browser, Chromium, zh-CN, as clm.admin)
Editing another user's active contract used to rely on
modifyAllRecordsfor the row write. The steps:订单 — 黑貂保险(ORD-2026-0015, 生效中, 业务承办人 Business Requester 3).jurisdictionis written,updated_byis clm.admin, andowner_idis unchanged.The only console error during the run was one unrelated
404resource load, which also appears on origin/main. The edit form does not offer 批准时间, the 路由 flags, or 业务承办人. The form omitsowner_idfor every user: objectuiplugin-formautoLayout.tsdropsSERVER_OWNED_FIELD_NAMES, so this does not depend on permissions. In the console, reassignment is a REST write, measured above as 200. That was already true on origin/main.Gates (worktree head c50654e; each exit code captured before any pipe)
Baseline on origin/main b38df5a:
pnpm lintexit 0, "18 warning(s), 6 suggestion(s)", 17 of themsecurity-superuser-field-mask-inert.Acceptance notes
rowLevelSecurity(outside this card's surface), the comment oncontract_admin_edit_any_statusstill says "Modify All Data does not skip them on this object". That is still true, but the administrator no longer holds Modify All on the contract. The companion is now needed because neitherwriteScope: 'org'nor any super-user bit skips business RLS. A one-line wording follow-up is offered in the report.openAllExcept(Signature, [])andopen(Review, ['internal_note'])repeat what the children'smodifyAllRecordsalready votes since ADR-0066's 2026-11-02 note. They are harmless and left as they are.clm_requesterupdate on its own obligations, andcontrolled_by_parentrefuses it for all 200 — 我负责的履约 is read-only for the audience it is built for #64 edits theclm_obligationentry of the same file. Whichever PR lands second rebases.🤖 Generated with Claude Code
https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn