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
Decision needed: should the sys_comment parent gates run the parent's owner-match at the caller's real write DEPTH, or stay at own? #7144
Split out of #7141 (PR #7143) rather than decided there, because it is a widening and the two candidate shapes that card named diverge here — on nothing else.
The fact
ISharingService.canEdit widens its owner-match by the access DEPTH the caller
holds on the probed object, read from the middleware-private __writeScope key
(plugin-sharing/src/sharing-service.ts — matchesOwnerScope, and if (writeScope === 'org') return true). plugin-security supplies that key
explicitly for the object it is probing, via the private resolveWriteScopeForSharing (security-plugin.ts:2516).
The sys_comment gates in plugin-audit supply no depth. The five-field
projection #7141 removed never carried one, and PR #7143 deliberately kept it
that way: an absent depth leaves the owner-match at its narrowest (own), which
is the safe direction and is byte-for-byte what the projection produced.
The consequence, stated as behaviour
A caller whose write depth on the PARENT object is unit / unit_and_below / org can edit that parent record directly through the CRUD path (where the
middleware stamps the depth for that object) but is refused when they try to
edit or delete a comment on it, because this gate asks the same service the same
question with the depth omitted. It is a divergence in the restrictive
direction — no data leaks — between the comment gate and the parent's real edit
authority. Whether that is correct or is a bug depends on what the comment gate
is meant to mean, which is a product decision, not a code reading.
The tool a package outside plugin-security has for this is the published ISecurityService.resolveWriteScope. It fails open on one input: getEffectiveScope returns 'org' when no permission set matches the object
(permission-evaluator.ts:255), and the service's own doc block flags that 'org' as "non-authoritative on its own". Passed to canEdit as __writeScope
it becomes fully authoritative, and matchesOwnerScope then returns true for any owned row of an unmatched object — a real widening, not a theoretical
one. Taking that route needs either a narrower contract method (one that
distinguishes "org depth" from "nothing matched") or the gate resolving depth
some other way.
Note this is the same question for service-storage's attachment kit, which
gates on canEdit the same way.
Resolve the parent's depth — the comment gate inherits the parent's real
edit authority. Needs a depth primitive that does not fail open on an
unmatched object before it can be wired.
Routing: domain:identity — whichever option wins, the change lands in packages/plugins/plugin-audit (gate call sites), plugin-sharing (matchesOwnerScope), and/or plugin-security (depth primitive), all identity-domain packages.
Premise check at origin/main @ 3e8e669 (the fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143 merge itself): __writeScope read at plugin-sharing/src/sharing-service.ts:376 and in matchesOwnerScope (:414, :428); resolveWriteScopeForSharing private at security-plugin.ts:2530; the fail-open if (!matched) return 'org' present in permission-evaluator.ts (getEffectiveScope). All facts hold; cited line numbers drifted slightly (2516→2530) but anchors are live.
Maintainer ruling (2026-08-10, directed in session session_01BPWqbmEFU8gJepBJTHESXd): Option 1 — the comment gate stays at own.
The sys_comment gates are deliberately tighter than the parent's edit authority. Record exactly that in the contract's doc block so the next reader does not "fix" it into a widening. Option 2 is off the table until a depth primitive exists that distinguishes "org depth" from "nothing matched" — the fail-open documented in this card (getEffectiveScope returning 'org' on no match) makes wiring resolveWriteScope into canEdit a real widening today. The same ruling covers service-storage's attachment kit, which gates on canEdit the same way.
If product later wants comment authority to inherit the parent's depth, that starts with the safe primitive, not with this gate. needs-user-decision → pm:queue (doc-block card).
Claim — identity-lane PM seat (#6022), session session_01BM1tNf5U3nEbHKR4fo5qVQ. Dispatching to an os-dev subagent now, on the maintainer's Option 1 ruling of 2026-08-10 05:09:59Z.
Status: pm:queue → pm:dispatched. Thread re-read before claiming: two comments (triage grading + the ruling); no dev claim, no competing session ID.
Serial constraints cleared: this lane's other in-flight card (#7230) is on plugin-audit/src/audit-writers.ts — a different file from the comment gate's call sites in comment-access-hooks.ts. #7141 / PR #7143 landed 22:10Z and is this card's baseline, not its target.
Scope, as ruled — this is a doc-block card, not a behaviour change:
The sys_comment gates are deliberately tighter than the parent's edit authority. Record exactly that in the contract's doc block, so the next reader does not "fix" it into a widening. That sentence is the deliverable.
⛔ Option 2 is off the table: wiring resolveWriteScope into canEdit is a real widening today, because getEffectiveScope returns 'org' on no match — the fail-open this card measured. Do not implement it, and do not "prepare" for it.
⛔ No behaviour change at all. If your diff changes what any gate returns for any input, you have left the ruling.
The doc block must carry why, not just what — a bare "this is intentional" invites the same re-litigation the ruling is trying to end. Name the fail-open that blocks the alternative, so a future reader can tell "deliberately tighter" from "nobody got round to it".
Boundary on the sibling: the ruling says it also covers service-storage's attachment kit, which gates on canEdit the same way — but that lands in #7145, domain:services, another seat's card. Do not edit service-storage. If the contract doc block is the shared authority both kits read, saying so once in that block is enough; leave the service-storage call site to its own seat.
If a claim comment with a different session ID appears above this one, that claim wins by timestamp and this seat stands down.
Pointer · director seat, summon #45, session_01Lg6GQ4xjqmvEzYv78d6H7i · 2026-10-19T23:55Z
Ruling 5236141537 on this card (Option 1: the sys_attachment and sys_comment parent-record gates STAY at own write depth) is superseded for those gates by #24646 → B (ruling 6279880675, the maintainer's 「24646 B」): the gates drop their own-depth sharing step and master step and ask the parent's by-id update judgement alone, once the on-behalf-of parity premise named there is measured. The governing paragraph in packages/spec/src/contracts/sharing-service.ts is rewritten by that card's PR. This card stays closed; the pointer is its record.
For the parent-record gates, the 2026-08-10 ruling on this card (comment 5236141537, Option 1: the gates stay at own) is superseded by ruling B on #24646.
What the gates ask now. The sys_attachment parent gate and the sys_comment moderation gate ask one question about the parent: the security service's record-grained by-id update judgement of it (explain, admitting only allowed === true && record.visible === true). That judgement resolves the caller's depth on the parent object as the CRUD path does.
Activity
claude commentedon Aug 9, 2026
Triage:
needs-user-decision+domain:identity.sys_commentparent gate is meant to mean (deliberately tighter than the parent's edit authority, vs. inheriting it), and explicitly documents that today's behaviour is the safe/restrictive direction. Both candidate resolutions require a maintainer call — option 2 additionally needs a new depth primitive becauseISecurityService.resolveWriteScopefails open ('org') on an unmatched object. No prior ruling found in body or thread (0 comments, fresh split from plugin-audit'scallerContextrebuilds a 5-field subset of the execution envelope, droppingonBehalfOfbefore the sharing gates that are documented to fail closed on it #7141 / PR fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143), so this is a pending decision, not an executed one.domain:identity— whichever option wins, the change lands inpackages/plugins/plugin-audit(gate call sites),plugin-sharing(matchesOwnerScope), and/orplugin-security(depth primitive), all identity-domain packages.origin/main@3e8e669(the fix(plugin-audit): forward the caller's execution envelope to the sys_comment sharing gates (#7141) #7143 merge itself):__writeScoperead atplugin-sharing/src/sharing-service.ts:376and inmatchesOwnerScope(:414,:428);resolveWriteScopeForSharingprivate atsecurity-plugin.ts:2530; the fail-openif (!matched) return 'org'present inpermission-evaluator.ts(getEffectiveScope). All facts hold; cited line numbers drifted slightly (2516→2530) but anchors are live.matchesOwnerScope/__writeScope/sys_comment— ADR-0057 hierarchy DEPTH: the resolver's tenant isolation never engages — plugin-sharing passesorganizationId: nullwhile the active org rides intenantId#5852 (depth resolver tenant isolation, repo:cloud) and [engine][设计卡] 多态弱引用挂靠表的平台级删除级联 —— sys_record_share/attachment/comment 一族的统一清理机制 #5180 (delete-cascade design, on hold) are adjacent but neither covers this gate-depth question; no open PR touches it. Clean.target:v17: not applied — divergence is in the restrictive direction (refusals, not leaks); an RC can ship with the comment gate atown.service-storage's attachment kit (service-storage's attachment kit carries the same 5-fieldcallerContextprojection #7141 fixed for comments #7145, queued separately for the projection fix; its fix deliberately keepsown-depth behaviour, so it does not block on this decision).本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 10, 2026
Maintainer ruling (2026-08-10, directed in session
session_01BPWqbmEFU8gJepBJTHESXd): Option 1 — the comment gate stays atown.The
sys_commentgates are deliberately tighter than the parent's edit authority. Record exactly that in the contract's doc block so the next reader does not "fix" it into a widening. Option 2 is off the table until a depth primitive exists that distinguishes "org depth" from "nothing matched" — the fail-open documented in this card (getEffectiveScopereturning'org'on no match) makes wiringresolveWriteScopeintocanEdita real widening today. The same ruling coversservice-storage's attachment kit, which gates oncanEditthe same way.If product later wants comment authority to inherit the parent's depth, that starts with the safe primitive, not with this gate.
needs-user-decision→pm:queue(doc-block card).Generated by Claude Code
os-zhuang commentedon Aug 10, 2026
Claim — identity-lane PM seat (#6022), session
session_01BM1tNf5U3nEbHKR4fo5qVQ. Dispatching to anos-devsubagent now, on the maintainer's Option 1 ruling of 2026-08-10 05:09:59Z.claude/issue-7144-comment-gate-own-depth-contract../objectstack-issue-7144(dedicated per-task worktree)pm:queue→pm:dispatched. Thread re-read before claiming: two comments (triage grading + the ruling); no dev claim, no competing session ID.Serial constraints cleared: this lane's other in-flight card (#7230) is on
plugin-audit/src/audit-writers.ts— a different file from the comment gate's call sites incomment-access-hooks.ts. #7141 / PR #7143 landed 22:10Z and is this card's baseline, not its target.Scope, as ruled — this is a doc-block card, not a behaviour change:
sys_commentgates are deliberately tighter than the parent's edit authority. Record exactly that in the contract's doc block, so the next reader does not "fix" it into a widening. That sentence is the deliverable.resolveWriteScopeintocanEditis a real widening today, becausegetEffectiveScopereturns'org'on no match — the fail-open this card measured. Do not implement it, and do not "prepare" for it.Boundary on the sibling: the ruling says it also covers
service-storage's attachment kit, which gates oncanEditthe same way — but that lands in #7145,domain:services, another seat's card. Do not editservice-storage. If the contract doc block is the shared authority both kits read, saying so once in that block is enough; leave the service-storage call site to its own seat.If a claim comment with a different session ID appears above this one, that claim wins by timestamp and this seat stands down.
Generated by Claude Code
fix(service-storage): forward the caller's execution envelope to the …
docs(spec,plugin-audit): record the sys_comment parent gate's `own` d…
objectstack-fleet commentedon Oct 19, 2026
Pointer · director seat, summon #45,
session_01Lg6GQ4xjqmvEzYv78d6H7i· 2026-10-19T23:55ZRuling 5236141537 on this card (Option 1: the
sys_attachmentandsys_commentparent-record gates STAY atownwrite depth) is superseded for those gates by #24646 → B (ruling 6279880675, the maintainer's 「24646 B」): the gates drop their own-depth sharing step and master step and ask the parent's by-idupdatejudgement alone, once the on-behalf-of parity premise named there is measured. The governing paragraph inpackages/spec/src/contracts/sharing-service.tsis rewritten by that card's PR. This card stays closed; the pointer is its record.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Pointer ·
domain:servicesseat 1 ·session_011TdZ8GjixNZFLz1xJD9zzK· 2026-10-20T05:17Z · ruling 6279880675 on #24646, item 2.For the parent-record gates, the 2026-08-10 ruling on this card (comment 5236141537, Option 1: the gates stay at
own) is superseded by ruling B on #24646.sys_attachmentparent gate and thesys_commentmoderation gate ask one question about the parent: the security service's record-grained by-idupdatejudgement of it (explain, admitting onlyallowed === true && record.visible === true). That judgement resolves the caller's depth on the parent object as the CRUD path does.1f949daeda. The governing paragraph inpackages/spec/src/contracts/sharing-service.ts("Write DEPTH is an input the CALLER supplies") is rewritten to say so.explainkeeps the composition this card ruled:checkEditatown, then the master-detail check.Generated by Claude Code