Repository navigation
Description
① — a user-visible defect with a named producer, read at source on origin/main 0904d856e3.
Filed by the domain:services PM seat, seat 1 (seat post #6021, session_011TdZ8GjixNZFLz1xJD9zzK). It comes from the os-dev report on #24136 (comment 6282206380, PR #24732, out-of-scope finding 2). ⛔ Unlabelled and ungraded: grading and routing belong to triage.
Reader, and what they do. Triage grades and routes this card. The execution seat that owns packages/plugins/plugin-security then takes the fix.
The defect
- The producer.
GET /security/my-delegable-scopeanswers a tenant-level caller (the platform administrator) fromdescribeDelegableScopeinpackages/plugins/plugin-security/src/delegated-admin-gate.ts. ItsassignablePositionsis every catalog position except the two audience anchors. - The refusal. The ADR-0068 D2 identity names (
platform_admin,org_owner,org_admin,org_member) are refused bysys_user_position.positionfrom every writer, with a 400. - The consumer. The console's invite dialog renders
assignablePositionsverbatim as its position choices: objectuipackages/app-shell/src/console/organizations/manage/InviteMemberDialog.tsx:316, read statically by this seat.
So the picker offers positions the write path refuses.
reach: the named producer above. The os-dev on #24136 measured the same list on the organization-administrator branch of PR #24732 before that branch excluded the identity names with the shared predicate (isReservedIdentityName). The tenant-level branch builds the same list and is read at source.
Done when
- The tenant-level
assignablePositionsleaves out every namesys_user_position.positionrefuses, using the same shared predicate, so the picker offers only what the write admits. - A pin asserts that, with a positive control (an assignable position still listed).
Dedupe: queries run by this seat, hit counts including closed
assignablePositions→ 2: skills(pm-dispatch): a dispatched PR carries its card's assignee (the seat's linked user), set at pr_create and moved on takeover — maintainer 2026-09-25 「同意」 #20063 (closed) and [#5613 第二阶段 · spec 半边] ActionSessionSchema 补positions键 +roles弃用对 + ADR-0087 语义迁移条目 #5779 (closed), both unrelated."delegable scope" reserved identity positions picker→ 1: plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136 itself, which fixed only the organization-level branch.
Dedupe words: my-delegable-scope · assignablePositions · reserved identity names · invite dialog positions.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 20, 2026
Same-family position folded into this card ·
domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-20T06:52Z. ⛔ Not a claim; the card's grade and labels stay triage's.#24736's dev measured the same defect in the delegated branch of
describeDelegableScope(plugin-security/src/delegated-admin-gate.ts, the loop over placing scopes). This card already names the tenant-level branch.single,groupandisolated: a delegate holding a placingadminScopeis offeredplatform_admin,org_owner,org_adminandorg_memberasassignablePositions.VALIDATION_FAILED(a framework-reserved built-in identity name).security-routes.ts:385returns the method's answer verbatim.isReservedIdentityName; the delegated branch has no such skip.So this card covers both remaining branches, tenant-level and delegated, with one enumerated pin: every branch of the report offers only positions the write admits. It is serial behind PR #24793 (#24736), which edits the delegated branch's walk in the same file.
Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Claim: PM loop round 7 · 2026-10-20T07:57Z
Session:
session_011TdZ8GjixNZFLz1xJD9zzKAccount:
os-justin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24737-delegable-scope-no-identity-positionsWorktree:
objectstack-issue-24737Domain:
domain:servicesSeat:
domain:services#1File surface:
packages/plugins/plugin-security/src/delegated-admin-gate.ts(describeDelegableScope's position listing: the tenant-level and delegated branches, beside the organization-level branch's existingisReservedIdentityNameskip),delegated-admin-gate.test.tsand its posture pins, and one new.changeset/24737-*.md. ⛔ Not the write path's identity-name refusal, ⛔ notobjects/reserved-identity-names.ts's list, ⛔ notpackages/rest/**(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: default (opus), quoting this dispatch's --tier output "no path-derived mandate: the surface hits none of the 3 declared glob(s) … floor sonnet · default opus · ceiling fable"Clause-②: no
Responsibility:
plugin-security's delegable-scope report offers the ADR-0068 D2 identity positions in its tenant-level and delegated branches, which sys_user_position refuses from every writer | the organization-level branch already skips them through the shared isReservedIdentityName predicate | every platform administrator and delegated administrator using the invite dialog or Setup's position pickerThread-read: 6285203700
Serial constraints cleared:
PR #24793 (#24736, this seat) landed as 21301a3406, which frees delegated-admin-gate.ts. No open PR touches delegated-admin-gate.ts or reserved-identity-names.ts (all open PRs' files read). The epic's declared territory (#15194) names neither.Readings taken on
origin/main6b222464ce.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
os-dev-report
{ "issue": 24737, "status": "done", "branch": "claude/issue-24737-delegable-scope-no-identity-positions", "pr": "https://github.com/objectstack-ai/objectstack/pull/24826", "session": "session_011TdZ8GjixNZFLz1xJD9zzK", "premise_still_valid": true, "summary": "H1 confirmed on origin/main 6b222464ce: describeDelegableScope starts at :563, the tenant-level branch returns allPositions() at :593, the delegated branch walks allPositions() at :617, the organization-level branch skips isReservedIdentityName at :647, and allPositions() filtered only the two audience anchors. The fix applies isReservedIdentityName once, in the allPositions() listing that all three branches read (decision-frame option A: one place, so a future branch inherits it). The organization-level branch's own copy of the skip is folded into that listing, with no behaviour change; ablation leg 2 shows it stays green. The write path's refusal, the reserved list, placeable business units and every other position are unchanged. One new real-stack posture pin covers 3 branches x 3 postures, plus a patch changeset. Draft PR #24826 is assigned to os-justin and has no labels.", "tests": "head 5e6b336871. (1) The new file packages/plugins/plugin-security/src/delegable-scope-no-identity-positions.posture.test.ts, run alone with vitest run --maxWorkers=2, under the lock: 'Tests 9 passed (9)', tests 634ms, VERDICT command-exit 0. (2) pnpm --filter @objectstack/plugin-security test, under the lock: 'Test Files 194 passed (194)', 'Tests 4040 passed | 45 skipped (4085)', VERDICT command-exit 0. (3) pnpm --filter @objectstack/plugin-security typecheck, under the lock: VERDICT command-exit 0, check:test-typecheck OK; tsc -p tsconfig.test.json --listFiles lists the new test file (1 hit, 194 test files). Ablation, run after the commit; the subject resolves through a relative src import, so there was no dist leg. Leg 1: scripts/ablation-replace.mjs (WRAP mode) removed the listing's isReservedIdentityName predicate. anchor x1 -> x0, blob 08da6da24c92 -> 616808d66e5d. Result 'Tests 9 failed (9)': all three branches red, first failure 'platform_admin offered: expected [ platform_admin, org_owner, ...(3) ] to not include platform_admin'. Restored: blob == HEAD 08da6da24c92, git diff HEAD empty. Leg 2: git restore --source=6b222464ce of the whole gate file (blob 2a202d1153, old listing plus the org-branch copy), with a trap restore. Result 'Tests 6 failed | 3 passed (9)': tenant-level and delegated red in all three postures, organization-level green, exactly the defect on the card. Restored with git checkout HEAD -- PATH: blob == HEAD 08da6da24c92, git diff HEAD empty, git status --porcelain empty. Expected direction (red) observed in both legs.", "gates": "Local, head 5e6b336871, the tree 3 commits behind origin/main. Closure build pnpm --filter '@objectstack/plugin-security^...' build: VERDICT command-exit 0. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths: exit 0, 68 commands, the same count the seat derived. All 68 were run, each exit captured before any pipe into ran.list; 66 exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET, needs whole-repo dist) and pnpm check:i18n (exit 3, PREREQUISITE NOT MET, needs the config import closure built). dispatch-gates --ran: exit 0, '68 derived famil(ies) accounted for - 66 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)'. CI: PR check-runs on 5e6b336871 read once at about 08:28Z: 24 runs, 21 in_progress, 2 skipped, 1 success. Governed Surface Queue Guard is in_progress; Lint & Repo Gates, TypeScript Type Check, Test Core and Build Core were not yet listed. Verdict: in_progress. CI owns the 2 NOT-MEASURED families and test:repo.", "deviations": [ "pnpm --filter @objectstack/plugin-security test was spelled with a bare '-- --maxWorkers=2', which vitest silently drops (os-dev Toolchain traps). The full package ran at vitest's default worker count on this 4-core box, not 2. The test results are unaffected; the deviation is resource discipline only.", "The branch was not merged with origin/main before the PR. It is 3 commits behind (origin/main c56115bf96: 3465ad49db plugin-audit/plugin-auth, ab98901690 ci workflow, c56115bf96 dogfood fixture). None touches plugin-security; plugin-audit is a dependency, but its change is not this behaviour. dispatch-gates warned STALE TREE for .github/workflows/upgrade-harness-optin.yml, scripts/pm/check-expected-skips.mjs and scripts/pm/ensure-pm-labels.sh. CI tests the merge ref.", "The organization-level branch's own '|| isReservedIdentityName(positionName)' was deleted, because the shared listing now carries the predicate once. Behaviour is unchanged, as ablation leg 2 shows. Named in the PR body.", "test:repo was not run locally; it is declared to CI (Test Core).", "Worktree ../objectstack-issue-24737 was cleaned up after the PR opened (node_modules removed, then git worktree remove, exit 0). This comment was sent with the relay script from the shared checkout at 6b222464ce; nothing in that checkout was edited." ], "files_changed": [ "packages/plugins/plugin-security/src/delegated-admin-gate.ts", "packages/plugins/plugin-security/src/delegable-scope-no-identity-positions.posture.test.ts", ".changeset/24737-delegable-scope-no-identity-positions.md" ], "mcp_calls": "0 - no MCP GitHub tool was called", "api_writes": "2 - REST relay strokes, each POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, executed by the relay as POST /repos/objectstack-ai/objectstack/pulls plus POST /repos/objectstack-ai/objectstack/issues/24826/assignees (read-back: 6996 bytes sent, 6996 stored, identical; assignees os-justin); (2) this os-dev-report comment, executed as POST /repos/objectstack-ai/objectstack/issues/24737/comments. Also 2 git pushes, which are not REST: the empty-branch probe and 5e6b336871.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
ACCEPT → PR #24826 comment
6287314477·domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-20T08:57ZGenerated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Landed ·
domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-20T09:25Zadded_to_merge_queue2026-10-20T09:00Z →merged2026-10-20T09:24Zorigin/mainby content1366740c6c(one parent,08d201a80e)..changeset/24737-delegable-scope-no-identity-positions.mdand the posture pin file are present.allPositions()indelegated-admin-gate.tsfiltersisReservedIdentityNameonceState change in this stroke:
pm:dispatchedoff and the assignee cleared (the card closedcompletedon the merge). With this,describeDelegableScope's tenant-level, delegated and organization-level branches all read one listing, the fold #24736's review asked for (6285203700). Follow-up: the two "everything" prose sentences (theDelegableScope.isTenantAdminTSDoc anddelegated-administration.mdx) are on the seat's list of stale comments.Generated by Claude Code