Skip to content

Every member holds clm_requester by default; the app declares its eleven capability tokens - #122

Merged
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-11-requester-default
Oct 25, 2026
Merged

zhuangjianguo merged 1 commit into
mainfrom
claude/issue-11-requester-default

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #11 — ruling step 3 (A): every member holds clm_requester by default; the app declares its eleven capability tokens

#11 stays open: the §04 gate × holder table (ruling step 1) rides the seat's governed PR #120. Ruling-ref: issue comment 6398381229 (batch #6 item 1, letter A), the retraction pre-written in 5604446794.

What changed

File Change
objectstack.config.ts New capabilities: block (only this block): the eleven authorization tokens the five permission sets grant in systemPermissions, each scope: 'org' with a label and a description — the five clm_*.access group gates and approve_contract · execute_contract · archive_contract · terminate_contract · manage_clauses · manage_approval_rules. No requires:, plugin or service is added.
src/profiles/requester.profile.ts isDefault: true; the header's "how the default is distributed" section is rewritten as "how the default reaches every member" (the everyone anchor, why the declaration is the other half of the flag).
src/sharing/positions.ts The seven RequesterSet.name entries are removed (the executive and GM positions now name no set, so their permissionSets key is omitted); the RequesterSet import goes; the header comment is rewritten.
README.md See below.

admin.profile.ts is untouched (ruling step 6: its own update rules from PR #117 stay, since the administrator still holds clm_requester).

README

  • Lines 93–97 (requesters hold no position, granted in Setup one by one) → every member holds clm_requester by default through the everyone anchor; creating the account is the whole of it. The named-accounts table's "Give it" cell for the three requesters now reads "nothing — every member already holds clm_requester".
  • Lines 146–152 (the dev admin holds no clm_* set and is served navigation: []) → the dev admin holds clm_requester like every member and is served 我的合同 and 分析, and no other group; the "rows parked on the dev admin" warning is restated for what is still true.
  • The permission_set_id drift (Assigning a position from a script): the sentence claiming sys_user_permission_set.permission_set_id is id-typed is replaced — on 18.1 the column is permission_set, text, carrying the set's name (read from GET /api/v1/meta/object/sys_user_permission_set on this branch's boot: permission_set text, user_id lookup sys_user; POST /api/v1/data/sys_user_permission_set {"user_id": …, "permission_set": "clm_requester"} → 201).
  • Two more paragraphs in that section that this change makes false, so they are corrected in the same file: "a name-spelled row grants that position's own set and clm_requester, because every position names that set too" (it no longer does — membership brings clm_requester, and explain attributes it to the "additive baseline"), and "an id in the position field lands the account in the dev admin's state: no clm_* set, navigation: []" (it now lands as a plain member: clm_requester alone, 我的合同 and 分析 only).
  • The retired bind-position-sets.ts name: already gone from README.md on 4460d31 — PR Upgrade the @objectstack/* dependencies from 17.7.0 to 18.1.0, walking the official upgrade checklists of 18.0 and 18.1 #110 (7b736a0) replaced it with src/sharing/positions.ts (git show 7b736a0 -- README.md), so README needs no change there. The two remaining mentions are in src/apps/clm.app.ts comments, outside this PR's surface (Acceptance notes).

Measurements — 18.1.0, pnpm demo --port 3411, one throwaway local database

Before = origin/main 4460d31; after = this branch d96e62a; same database for both. Persona accounts were created the README way on that database as the dev admin (POST /api/v1/auth/admin/create-user ×9 → 200; POST /api/v1/data/sys_user_position ×7 and POST /api/v1/data/sys_user_permission_set ×1 with permission_set: "clm_requester" → 201; each persona's password then changed through POST /api/v1/auth/change-password → 200). No permission prompt refused it.

Effective permission sets — POST /api/v1/security/explain principal, clm_contract read and create

Persona Before After
clm_admin position clm_admin, clm_requester, member_default same
clm_legal_counsel · clm_legal_head clm_legal, clm_requester, member_default same
clm_finance_controller clm_finance, clm_requester, member_default same
clm_records_manager clm_records, clm_requester, member_default same
clm_executive · clm_general_manager clm_requester, member_default same
requester (a sys_user_permission_set row) clm_requester, member_default same
no position, no grant (Persona Blank) member_default — read false, create false clm_requester, member_default — read true, create true
dev admin admin_full_access, member_default, organization_admin_no_bypass admin_full_access, clm_requester, member_default, organization_admin_no_bypass

Every position persona's set list is identical before and after. The only difference for them is attribution: explain names clm_requester "via direct grant" before and "via additive baseline (ADR-0090 D5)" after.

What the personas are served (GET /api/v1/meta/app/clm navigation groups, GET /api/v1/data/clm_contract?count=true total)

Persona Before After
admin · counsel · head 120 rows; own group + My Contracts + Analytics same groups; 121 rows (the blank member's new contract below)
finance · records 82 · 82 rows; own group + My Contracts + Analytics same
executive · GM 23 · 4 rows; My Contracts + Analytics same
requester 0 rows (owns none on this database); My Contracts + Analytics same
no position, no grant 403 on the list; navigation: [] 200; My Contracts + Analytics
dev admin navigation: [] My Contracts (Launch a Contract · Launched by Me · Waiting on Me · My Obligations) + Analytics

A brand-new member creates a contract: as Persona Blank, POST /api/v1/data/clm_contract {title, contract_type, party, our_entity} → before 403 PERMISSION_DENIED ("You do not have permission to perform this action…", object clm_contract); after 201, and the row then appears in that member's list (total 1).

Browser — Chromium at /opt/pw-browsers/chromium, Playwright, signed in as admin@objectos.ai

Opened /_console/, signed in, went to /_console/apps/clm.

  • Before (4460d31): the app's sidebar carries none of My Contracts, Launched by Me, Waiting on Me, My Obligations, Launch a Contract, Analytics, Executive Overview.
  • After (d96e62a): the app opens on /_console/apps/clm/clm_contract/view/my_contracts; the sidebar shows My Contracts (Launch a Contract, Launched by Me, Waiting on Me, My Obligations) and Analytics (Executive Overview) and no Legal, Finance, Records or Administration group. Clicking Launched by Me lands on the my_contracts view of clm_contract with the dev admin's rows grouped by status. (我的合同 is that group's zh-CN label.)
  • Browser console on both runs, identical: one 401 on GET /api/v1/auth/get-session (before sign-in) and one 404 resource load. Nothing new.

Boot

The boot diagnostics block (timestamps stripped) and every WARN/ERROR line of the server log are byte-identical between a re-run of pnpm demo on main and on this branch against the same database: Plugin superseded: 'com.objectstack.metadata', and the seeder's deferred-reference summary for the persona names this database does not have (Business Requester 1 …, the README's documented empty columns). No refusal line, no high-privilege / security-anchor line. objectstack dev --compile (both boots of pnpm demo) compiled.

The refusal leg — isDefault: true without the capabilities: block fails pnpm lint, pnpm validate and the dev compile with security-anchor-high-privilege — was measured on 18.1.0 in PR #117 (Leg A) and is not re-run here.

Gates — head d96e62a, exit codes captured to files before any pipe

pnpm install         exit 0
pnpm validate        exit 0   ✓ Validation passed (959ms)
pnpm lint            exit 0   6 suggestion(s), 0 errors   (same count as main per PR #117)
pnpm typecheck       exit 0   tsc --noEmit
pnpm lint:i18n-gate  exit 0   ✓ i18n gate · COVERAGE : 0 missing keys across 2 locale(s)

validate's seven warnings are the pre-existing ones (hierarchy-security edition note and the approval-slate approval-approvers-may-resolve-empty notes); none names a capability, clm_requester or isDefault.

Acceptance notes

  • src/apps/clm.app.ts still names the retired bind-position-sets.ts twice in comments (the header's "Every position also holds clm_requester (bind-position-sets.ts)" and the leadership-rungs note in the Analytics group). Both are now doubly stale — the binding is neither that file nor the positions any more, it is the default set. Outside this PR's surface; the seat's Implementation notes are shown to users as help text — field and dashboard-widget descriptions cite hook files, card numbers, DESIGN.md sections and PRs #95 remainder takes them.
  • Three declared tokens have no enforcement point in this tree today: nothing declares requiredPermissions on archive_contract, manage_clauses or manage_approval_rules (approve_contract is consumed by the approval centre, as contract-lifecycle.actions.ts explains). The descriptions in the capabilities: block therefore call them "DESIGN.md §04 action gate for …" and do not claim a button they guard. Observation only.
  • The requester persona's sys_user_permission_set row is now redundant (it grants what membership already gives); harmless, and a deployment that granted clm_requester by hand can leave those rows or delete them.
  • security/explain attributes position-defined sets "via direct grant" (filed upstream by the seat after PR clm_admin edits contracts past submitted: the administrator carries its own permissive update policies #117); after this change it no longer touches clm_requester, which reads "additive baseline".

🤖 Generated with Claude Code

https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn

DESIGN.md §04 makes clm_requester every employee's default set. Until now the
app distributed it by hand: each of the seven positions named it next to its
own set, and an employee with no position needed a Setup grant. The platform
mechanism for a default set, `isDefault: true`, was refused because the set
grants system-permission tokens, which count as high-privilege unless the
stack declares them (ADR-0090 D5/D9, rule security-anchor-high-privilege).

18.1 excuses declared tokens, so the stack now declares the eleven
authorization tokens its five sets grant under `capabilities:` (scope org),
clm_requester is marked `isDefault: true`, and the seven position entries that
named it are removed. Every position persona's effective sets are unchanged;
a member with no position now holds clm_requester through the everyone
anchor, and so does the dev admin. README drops the per-person grant, the
"dev admin sees no My Contracts" passage and the id-typed permission_set_id
claim (the column is `permission_set`, a name).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nzWFRUo2U7evMkPdVniAn
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · head d96e62a · 2026-10-25T14:02Z

check reading
shape draft → main, Part of #11, no closing keyword; assignee zhuangjianguo
CI on the head both Validate check-runs success on d96e62a = PR head.sha; mergeable clean
scope 4 files (+105/−65): objectstack.config.ts gains only the capabilities: block; clm_requester gets isDefault: true; positions.ts drops the seven RequesterSet.name entries; README rewritten to match. Nothing governed
the ruling 6398381229 step 3, letter A — delivered. The eleven declared tokens are exactly the union of the five sets' systemPermissions (seat cross-check of all five profiles on the head), each scope: 'org'
measured (dev, 18.1.0) every persona's resolved sets unchanged before/after (only the attribution moves to "additive baseline"); a member with no position goes from 403 / navigation: [] to 我的合同 + 分析 and can create a contract; dev admin now sees 我的合同 + 分析; boot diagnostics byte-identical to main
not in this PR src/apps/clm.app.ts still names the retired bind-position-sets.ts twice → the #95 remainder; three declared tokens (archive_contract, manage_clauses, manage_approval_rules) have no requiredPermissions consumer yet — observation only

Landing: under the maintainer's merge authorization (project thread: 「你可以自己审核的就自己合并」).


Generated by Claude Code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants