You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
security/explain: sets a position grants through its definition's permissionSets are attributed "via direct grant", not to the position #25799
Filing class: defect (operator-visible diagnostics; effective access is correct) — reach: POST /api/v1/security/explain, measured on @objectstack/* 18.1.0 in objectstack-ai/hotclm (mainf5ad133, report on objectstack-ai/hotclm#11).
Symptom
A user who holds a position whose definition names its sets in permissionSets: [...] (ADR-0131 D3 shape) and has nosys_user_permission_set row gets an explain principal whose contributors read via: direct grant for those sets. Only a set whose name equals the position's own name reads position:<name>.
Measured: hotclm position clm_legal_counsel declares permissionSets: ['clm_legal', 'clm_requester']; a user with only that sys_user_position row → explain principal contributors clm_legal via direct grant, clm_requester via direct grant. Same for the finance, records and executive positions. A clm_admin position (same-named set) → position:clm_admin.
Expected
Contributors name the position that granted them (position:clm_legal_counsel), so an operator reading explain can tell an assignment row from a position definition — the distinction #24757 also needs in Setup.
Repro
A position with permissionSets: ['set_a'] where set_a ≠ the position name.
Assign a user only that position (no sys_user_permission_set row).
POST /api/v1/security/explain as or for that user → contributor set_a reads via: direct grant.
Effective access is correct (the sets are in force); only the attribution is wrong. Seam: plugin-security explain principal contributor attribution for position-defined sets. Related: #24757 (same-named set invisible in Setup), #24736 (position walk counting).
Filed by the objectstack-ai/hotclm PM seat from the hotclm#11 dev report.
p3: a diagnostics attribution only. Effective access is correct, as the card says. explain should attribute a definition-granted set to position:<name> (ADR-0131 D3), not to direct grant.
Claim: PM loop round 7 · card thread
Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01Be1V9UmuD2nUUncnpMQ5tc
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-xmha3v (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#1
Ruling-ref: diagnostics attribution only, effective access byte-identical; explain attributes a set a position's definition grants (ADR-0131 D3 permissionSets) to position:<positionName>; a sys_user_permission_set row keeps direct grant; no change to set resolution or to Setup (#24757's surface); intake under Leehom 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」
File surface: packages/plugins/plugin-security/src/explain-engine.ts and the context builder that carries a set's granting position (named in the report), their tests, one changeset. ⛔ No packages/spec/** (stop on breach; explain in the report)
Container & model: S, mode:thread, default model and effort (set at thread open)
Clause-②: no (expected: the explain via text changes for these contributors; the dev measures whether via is a declared closed vocabulary)
Metadata-protocol: none
Responsibility: explain reads via: direct grant for a position-defined set (card #25799, from the hotclm#11 dev report)
Thread-read: none
Premise: holds (read 2026-10-26T10:33Z at origin/mainbbf70614bd: card open, unassigned, pm:queue, no claim; viaOf in explain-engine.ts attributes position: only to a same-named set; the dev reproduces red first)
Serial constraints cleared: no open PR touches packages/plugins/plugin-security/src/explain-engine.ts; the dev re-measures before pr_create
Release · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-26T10:43Z · ⛔ Not a claim.
Claim 6413901032 (branch claude/project-thread-xmha3v) is withdrawn before any edit: server seat 2's PR #26064 (#24757) already changes explain-engine.tsviaOf to attribute a set to position:<P> exactly when P's definition names it in permissionSets, with pins for this card's case, the inverse (a direct grant sharing a position's name) and an unreadable catalogue. That PR carries this card (Fixes #25799); server seat 2 posts the claim naming its branch. pm:dispatched stays for that claim.
Claim: PM loop round 2 · card thread · carried by PR #26064 (#24757), taken over from seat 1's withdrawn claim 6413901032
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01J8LYP8UCJURxbFWZqnywyD (#24757's thread)
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-6wuxt3 (#24757's card-thread branch, the head of PR #26064; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#2
Ruling-ref: the card's expected result (contributors name the position whose definition granted the set); seat 1's order ruling for this card, carried over: a set a user holds both through a sys_user_permission_set row and through a position definition must not hide the direct row (it reads direct grant, or names both channels); intake of a p3 card rides the maintainer's 「p1 p2 处理完了,就可以处理 p3」 (2026-10-26T10:29Z)
File surface: packages/plugins/plugin-security/src/explain-engine.ts (viaOf, positionBindingsOf) and explain-position-set-attribution.test.ts, inside PR #26064's surface; one Fixes #25799 line in that PR's body. ⛔ No packages/spec/** (stop on breach; explain in the report)
Container & model: M, mode:thread, default model and effort (set at thread open)
Clause-②: no (narrowing) (this card adds none of its own; the arm is #26064's)
Metadata-protocol: none
Responsibility: operators reading explain see via: direct grant for a position-defined set (measured on hotclm, card body); effective access is correct
Thread-read: 6414088555
Premise: holds (read 2026-10-26T10:45Z: card open, pm:dispatched, assignee os-project-manager; seat 1's release 6414088555 withdrew its claim before any edit; PR #26064 head aa5c5b22 already changes viaOf but does not yet pin the dual-source case)
Serial constraints cleared: the only in-flight work on explain-engine.ts is PR #26064 itself; seat 1's dev stopped before pushing
Claim: PM loop round 2 · card thread · carried by PR #26064 (#24757), amended (surface: one core field), supersedes 6414107849
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01J8LYP8UCJURxbFWZqnywyD (#24757's thread)
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-6wuxt3 (#24757's card-thread branch, the head of PR #26064; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#2
Ruling-ref: the card's expected result (contributors name the position whose definition granted the set); seat 1's order ruling for this card, carried over: a set a user holds both through a sys_user_permission_set row and through a position definition must not hide the direct row (it reads direct grant, or names both channels); intake of a p3 card rides the maintainer's 「p1 p2 处理完了,就可以处理 p3」 (2026-10-26T10:29Z)
File surface: packages/plugins/plugin-security/src/explain-engine.ts (viaOf, positionBindingsOf), explain-position-set-attribution.test.ts, and packages/core/src/security/resolve-authz-context.ts (additive directPermissions on UserAuthzGrants and ResolvedAuthzContext, the names the resolver already holds as directly assigned; seat ruling A: the dual case names both channels, direct grant + position:P), all inside PR #26064's surface; one Fixes #25799 line in that PR's body. ⛔ No packages/spec/** (stop on breach; explain in the report)
Container & model: M, mode:thread, default model and effort (set at thread open)
Clause-②: yes (narrowing) (this card's additive core field widens a published type; the narrowing is #24757's, in the same PR)
Metadata-protocol: none
Responsibility: operators reading explain see via: direct grant for a position-defined set (measured on hotclm, card body); effective access is correct
Thread-read: 6414107849
Premise: holds (read 2026-10-26T10:56Z: card open, pm:dispatched, assignee os-project-manager; seat 1's release 6414088555 withdrew its claim before any edit; PR #26064 head aa5c5b22 already changes viaOf but does not yet pin the dual-source case; the dev measured that explain cannot tell the two channels apart without the resolver exposing its existing split)
Serial constraints cleared: the only in-flight work on explain-engine.ts is PR #26064 itself; seat 1's dev stopped before pushing
ACCEPT → PR #26064 comment 6420785523 (head df4eb65c48; contract review 6420762600 PASS; Tier H, approved by a governed approver and cleared by the maintainer once the reds were fixed) · domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-26T16:04Z. The head is green; landing through the queue now.
2026-10-26T18:20Z · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo. PR #26064 merged at 2026-10-26T18:18Z by the merge queue as 733b3c849a on main (Fixes #24757, Fixes #25799; re-ACCEPT 6422813697; contract review 6422799890, PASS; Tier H, approved by os-zhuang, landed on the maintainer's 「修正之后可以合并」). The merge closed this card as completed.
GET /repos/objectstack-ai/objectstack/issues/25799
closed, completed
Landed: a position no longer grants a permission set through its name alone (the name fold is retired); a set reaches a holder only through the position's declared permissionSets or a direct grant, and the fixtures that leaned on the fold were rebound to declared or direct grants with no assertion moved.
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
bug·priority:p3·domain:server·area:access·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T13:54Z⛔ Not a claim, ⛔ not a dispatch.
explainshould attribute a definition-granted set toposition:<name>(ADR-0131 D3), not todirect grant.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 7 · card thread
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_01Be1V9UmuD2nUUncnpMQ5tcAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-xmha3v(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: diagnostics attribution only, effective access byte-identical;
explainattributes a set a position's definition grants (ADR-0131 D3permissionSets) toposition:<positionName>; asys_user_permission_setrow keepsdirect grant; no change to set resolution or to Setup (#24757's surface); intake under Leehom 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」File surface:
packages/plugins/plugin-security/src/explain-engine.tsand the context builder that carries a set's granting position (named in the report), their tests, one changeset. ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
S,mode:thread, default model and effort (set at thread open)Clause-②: no (expected: the explain
viatext changes for these contributors; the dev measures whetherviais a declared closed vocabulary)Metadata-protocol: none
Responsibility: explain reads
via: direct grantfor a position-defined set (card #25799, from the hotclm#11 dev report)Thread-read: none
Premise: holds (read 2026-10-26T10:33Z at
origin/mainbbf70614bd: card open, unassigned,pm:queue, no claim;viaOfinexplain-engine.tsattributesposition:only to a same-named set; the dev reproduces red first)Serial constraints cleared: no open PR touches
packages/plugins/plugin-security/src/explain-engine.ts; the dev re-measures beforepr_createobjectstack-fleet commentedon Oct 26, 2026
Release ·
domain:serverseat 1 ·os-project-manager·session_01R1q2xdtYgU6FKrZ7LgjPCC· 2026-10-26T10:43Z · ⛔ Not a claim.Claim
6413901032(branchclaude/project-thread-xmha3v) is withdrawn before any edit: server seat 2's PR #26064 (#24757) already changesexplain-engine.tsviaOfto attribute a set toposition:<P>exactly when P's definition names it inpermissionSets, with pins for this card's case, the inverse (a direct grant sharing a position's name) and an unreadable catalogue. That PR carries this card (Fixes #25799); server seat 2 posts the claim naming its branch.pm:dispatchedstays for that claim.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 2 · card thread · carried by PR #26064 (#24757), taken over from seat 1's withdrawn claim 6413901032
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01J8LYP8UCJURxbFWZqnywyD(#24757's thread)Account:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-6wuxt3(#24757's card-thread branch, the head of PR #26064; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: the card's expected result (contributors name the position whose definition granted the set); seat 1's order ruling for this card, carried over: a set a user holds both through a
sys_user_permission_setrow and through a position definition must not hide the direct row (it readsdirect grant, or names both channels); intake of a p3 card rides the maintainer's 「p1 p2 处理完了,就可以处理 p3」 (2026-10-26T10:29Z)File surface:
packages/plugins/plugin-security/src/explain-engine.ts(viaOf,positionBindingsOf) andexplain-position-set-attribution.test.ts, inside PR #26064's surface; oneFixes #25799line in that PR's body. ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
M,mode:thread, default model and effort (set at thread open)Clause-②: no (narrowing) (this card adds none of its own; the arm is #26064's)
Metadata-protocol: none
Responsibility: operators reading explain see
via: direct grantfor a position-defined set (measured on hotclm, card body); effective access is correctThread-read: 6414088555
Premise: holds (read 2026-10-26T10:45Z: card open,
pm:dispatched, assigneeos-project-manager; seat 1's release6414088555withdrew its claim before any edit; PR #26064 headaa5c5b22already changesviaOfbut does not yet pin the dual-source case)Serial constraints cleared: the only in-flight work on
explain-engine.tsis PR #26064 itself; seat 1's dev stopped before pushingGenerated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 2 · card thread · carried by PR #26064 (#24757), amended (surface: one core field), supersedes 6414107849
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01J8LYP8UCJURxbFWZqnywyD(#24757's thread)Account:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-6wuxt3(#24757's card-thread branch, the head of PR #26064; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: the card's expected result (contributors name the position whose definition granted the set); seat 1's order ruling for this card, carried over: a set a user holds both through a
sys_user_permission_setrow and through a position definition must not hide the direct row (it readsdirect grant, or names both channels); intake of a p3 card rides the maintainer's 「p1 p2 处理完了,就可以处理 p3」 (2026-10-26T10:29Z)File surface:
packages/plugins/plugin-security/src/explain-engine.ts(viaOf,positionBindingsOf),explain-position-set-attribution.test.ts, andpackages/core/src/security/resolve-authz-context.ts(additivedirectPermissionsonUserAuthzGrantsandResolvedAuthzContext, the names the resolver already holds as directly assigned; seat ruling A: the dual case names both channels,direct grant + position:P), all inside PR #26064's surface; oneFixes #25799line in that PR's body. ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
M,mode:thread, default model and effort (set at thread open)Clause-②: yes (narrowing) (this card's additive core field widens a published type; the narrowing is #24757's, in the same PR)
Metadata-protocol: none
Responsibility: operators reading explain see
via: direct grantfor a position-defined set (measured on hotclm, card body); effective access is correctThread-read: 6414107849
Premise: holds (read 2026-10-26T10:56Z: card open,
pm:dispatched, assigneeos-project-manager; seat 1's release6414088555withdrew its claim before any edit; PR #26064 headaa5c5b22already changesviaOfbut does not yet pin the dual-source case; the dev measured that explain cannot tell the two channels apart without the resolver exposing its existing split)Serial constraints cleared: the only in-flight work on
explain-engine.tsis PR #26064 itself; seat 1's dev stopped before pushingGenerated by Claude Code
feat(core,plugin-security): explain names both channels for a set hel…
feat(plugin-security): a human self-explanation reads the resolver's …
test(core,plugin-security): directPermissions in the envelope goldens…
chore(gates): the pinned engine-double ledger records the new self-ex…
fix(tests): envelopes carry directPermissions; the verify fixture dec…
objectstack-fleet commentedon Oct 26, 2026
ACCEPT → PR #26064 comment 6420785523 (head
df4eb65c48; contract review6420762600PASS; Tier H, approved by a governed approver and cleared by the maintainer once the reds were fixed) ·domain:serverseat 2 ·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-10-26T16:04Z. The head is green; landing through the queue now.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Landed
2026-10-26T18:20Z ·
domain:serverseat 2 ·os-project-manager·session_0186vfjHHMsRXC9EUT9hTFMo. PR #26064 merged at 2026-10-26T18:18Z by the merge queue as733b3c849aonmain(Fixes #24757,Fixes #25799; re-ACCEPT6422813697; contract review6422799890, PASS; Tier H, approved byos-zhuang, landed on the maintainer's 「修正之后可以合并」). The merge closed this card as completed.git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26064-*'git merge-base --is-ancestor 733b3c849a origin/mainGET /repos/objectstack-ai/objectstack/issues/25799completedpermissionSetsor a direct grant, and the fixtures that leaned on the fold were rebound to declared or direct grants with no assertion moved.v6.0.0or later before a framework pin crosses this release) is cloud-lane card cloud: move every hosted HotCRM to v6.0.0 or later before the framework pin crosses the release carrying #26064, or its five same-named positions lose their permission sets #26141; spec: retire PositionGrantVia 'name' (the position-name fold's grant mark) once #24757's runtime retirement lands #26022 follows for the published-but-unemittedPositionGrantVia'name'.Generated by Claude Code