Skip to content

docs: the position census disagrees with itself in three places — README says seven over a table of six, DESIGN.md §11 says both 7 and 8 - #69

Merged
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-58-position-census
Sep 10, 2026
Merged

docs: the position census disagrees with itself in three places — README says seven over a table of six, DESIGN.md §11 says both 7 and 8#69
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-58-position-census

Conversation

@zhuangjianguo

Copy link
Copy Markdown
Collaborator

Fixes #58

Four passages stated the position/permission-set census. Two were right, and this PR makes the other two agree with the tree. The fourth was found by the grep the card mandates and is called out below.

Claim to reading

Every count in this body, and every count the diff writes, backed by a reading taken on this branch.

Claim Reading
7 positions src/sharing/positions.ts, CLM_POSITION: clm_legal_counsel · clm_legal_head · clm_finance_controller · clm_executive · clm_general_manager · clm_records_manager · clm_admin
5 permission sets src/profiles/index.ts — 5 exports; grep -c "name: 'clm_" src/profiles/*.profile.ts = clm_admin · clm_finance · clm_legal · clm_records · clm_requester
6 sharing rules grep -c 'defineSharingRule({' src/sharing/contract.sharing.ts = 6
11 objects (unchanged in the row I edit) ls src/objects/*.object.ts | wc -l = 11
"assign the seven positions" now true the Position column holds exactly the 7 CLM_POSITION values — each one checked back against positions.ts, all 7 present, nothing else in the column
"The other six positions are named however you like" 7 positions minus the 1 that must be named exactly (clm_legal_counsel) = 6
"Five of the accounts above have to be named exactly" (unchanged count) the exact-name table: Business Requester 1..3 + Legal Counsel 1..2 = 5 accounts
clm_admin is the only set carrying clm_admin.access grep -rn 'clm_admin.access' src/ — one declaration, src/profiles/admin.profile.ts:40
the app's Administration group is gated on it src/apps/clm.app.ts:283requiredPermissions: ['clm_admin.access'], children clm_approval_rule and clm_party
clm_admin is the only position that may write clm_contract_type / clm_approval_rule all four other sets declare allowCreate: false, allowEdit: false, allowDelete: false on both; admin.profile.ts declares config (CRUD)
the clm_admin position is the only route to that set src/security/bind-position-sets.ts, BINDINGS: [CLM_POSITION.admin, AdminSet.name]
business requesters hold no position src/profiles/requester.profile.ts header: "an employee who holds no position receives the set in Setup (a sys_user_permission_set grant), which is the platform's supported path"

The two judgement calls

1. How (business requesters) stops sitting in a Position column. Moved out of the table into a paragraph directly under it, not into a second row group or a second table. The reason is that it is not a weaker kind of position — it is a different object. A position is a sys_user_position row keyed by name; the requester default is a sys_user_permission_set grant. requester.profile.ts documents why it cannot be implicit: isDefault: true would auto-bind the set to the everyone anchor, but the set carries a system permission, so the runtime refuses the binding and os lint refuses the declaration outright. A row group inside the same table would still invite the reader to type the value into sys_user_position.position, which is exactly the failure the card names. The paragraph says the object and the screen instead, and keeps the row's three facts (three accounts, the default clm_requester set, launches contracts and sees their own).

2. Whether an operator should assign clm_admin. Yes — and this is the more consequential half of the fix. Not merely because seven is the true count, but because the section as written left the demo with no CLM administrator at all:

  • clm_admin is the only permission set carrying clm_admin.access, and the clm_admin position is the only declared route to it (bind-position-sets.ts).
  • That token gates the app's Administration group. With nobody holding the position, group_admin and its two items are served to no one — a whole navigation group invisible in the demo.
  • It is the only set that may write clm_contract_type and clm_approval_rule; the other four are read-only on both. So nobody could maintain contract types or the approval matrix.
  • The README's own next paragraph is the clincher: "The dev admin is deliberately none of them: it holds no clm_* permission set", served navigation: []. That paragraph is correct and stays — but it means the one account pnpm demo creates cannot be the administrator. A real administrator account is precisely what the section was missing, and the card's own framing agrees.

The new row states what it actually grants, in the position's own words from positions.ts plus the two reach facts above.

The fourth occurrence

The card asks for every occurrence, not the three it names. grepping both files for position and permission-set counts turned up a fourth in DESIGN.md §10:

用户不可种子;各岗位账号在 Setup 建用户后分配 position(法务经办 ×2、法务负责人、财务负责人、分管领导、总经理、档案与记录管理员、业务承办 ×3)。

Same defect as the README table, both halves: 业务承办 ×3 sits inside a position enumeration although it is not a position, and clm_admin is absent. It also contradicts its own document — governed §04 already says "Position 扁平,七个" and "clm_requester(所有员工默认)". Fixed in the same shape as the README, one line. Leaving it would have satisfied the card's §11 acceptance while the document still disagreed with itself about exactly this. Flagging it explicitly since it is outside the three the card names — drop the hunk if the seat would rather it were its own card.

Two passages were already right and are untouched: §11's layout block (5 permission set · 7 position · 6 sharing rule · FLS) and §04's census.

Scope

  • DESIGN.md changed lines are 327 and 352 only (git diff -U0 hunk headers). §01–§04 spans lines 24–205 and §09 spans 293–309 — both untouched, and no other §11 row is touched. DESIGN.md §09 still specifies 各阶段合同数**漏斗** — the app stopped shipping a funnel in #48 and narrowed what it plots in #59 #63 is unaffected.
  • The operator narrative is not reorganised beyond what the count fix forces. The one non-count edit is "Five of these accounts" becoming "Five of the accounts above": moving the requesters out of the table put a paragraph between that sentence and its referent, and "these" would otherwise have read as the three requesters. The measurement (5) is unchanged and still correct.
  • No changeset (this repo has no changeset gate). No browser run owed — the diff changes no runnable surface.

Gates

All four run in the foreground, each redirected to its own file with the exit code read immediately, no pipe between the command and $?.

Gate Exit
pnpm validate 0
pnpm lint 0
pnpm typecheck 0
pnpm lint:i18n-gate 0
> hotclm@0.1.0 typecheck
> tsc --noEmit
✓ i18n gate
  LOCALES  : "en", "zh-CN" checked (required: en, zh-CN)
  COVERAGE : 0 missing keys across 2 locale(s)

lint ends 21 warning(s), 5 suggestion(s) (994ms), validate carries its carrier-only field notes — both pre-existing on main, both warnings, both exit 0.

None of these four gates reads DESIGN.md or README.md, so none of them verified this change and none of them could have moved. Demonstrated, not just argued, three ways:

  1. grep -il DESIGN over the four gate logs — no file matched (exit 1). Same for grep -il README — no file matched (exit 1).
  2. tsc --noEmit --listFiles emits the whole compiled program: 0 files ending .md, out of 305 files from this repo. tsconfig.json includes only objectstack.config.ts and src/**/*.
  3. git diff --name-only is exactly DESIGN.md and README.md — the diff has no intersection with what any gate walks (validate and lint walk the compiled metadata graph; the i18n gate walks the locale bundles).

The evidence that this change is right is the claim-to-reading table above, not the gates.

grep -naP for control bytes over both edited files: clean (exit 1).

🤖 Generated with Claude Code

https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR


Generated by Claude Code

The tree defines seven positions and five permission sets. Four passages
across README.md and DESIGN.md stated that census and only two were right.

README.md's operator setup table promised "the seven positions" over seven
rows whose seventh was `(business requesters)` — a default permission set,
not a position — while the seventh real position, `clm_admin`, was absent.
The count reconciled arithmetically (6 positions + 1 set row = 7 rows),
which is why it survived. Two costs: an operator following it literally
never assigns `clm_admin`, and a non-position sat in a column headed
`Position` whose values are typed into `sys_user_position.position`.

Add `clm_admin` as the seventh row and move the requesters out of the table
into a paragraph that names the mechanism they actually use — a
`sys_user_permission_set` grant, a different object from `sys_user_position`
(`src/profiles/requester.profile.ts` documents the distributed default:
`isDefault` cannot be used because the set carries a system permission).
The `Position` column now contains only real `CLM_POSITION` values.

An operator SHOULD assign `clm_admin`: `src/security/bind-position-sets.ts`
binds that position to `AdminSet`, the only set carrying `clm_admin.access`,
which gates the app's Administration group (`src/apps/clm.app.ts`), and the
only set that may write `clm_contract_type` and `clm_approval_rule`. The dev
admin deliberately holds no `clm_*` set, so without this row the demo has no
account that can maintain the configuration objects.

"The other five positions" follows the table: 7 - 1 exactly-named = six.

DESIGN.md §11's M1 row said `8 position / 6 set`; the layout block four
lines above it already said `7 position` and `5 permission set` and is
untouched. §10's seeding sentence, found by the grep this card mandates,
carried the same defect as the README table: `业务承办 ×3` listed inside a
position enumeration with `clm_admin` absent.

DESIGN.md §01-§04 and §09 are untouched: the only changed lines are 327 and
352. No gate reads either file, so none can have moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
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.

docs: the position census disagrees with itself in three places — README says seven over a table of six, DESIGN.md §11 says both 7 and 8

2 participants