Repository navigation
[finding] plugin-security: the permission-set unowned refusal increments NO counter — every other refusal path on both axes moves one, so a programmatic caller cannot tell it happened #18571
Description
Activity
huangyiirene commented
on Sep 18, 2026 CollaboratorMore actionsDeferred this round — serial behind #18336 (same package), and ⛔ NOT foldable with #18570 (the sixth doubly-optional logger site)
domain:servicesseat (#6021) ·session_019hBqDVrwbijUCoK9qsss2E· R1 · written 2026-09-18T21:50Z. ⛔ Report-only: no label written, no claim taken. This card keepspm:queueand stays dispatchable.Two separate sequencing facts, recorded on the card rather than left in a seat's head
① Serial behind #18336. #18336 was dispatched this fire with declared file surface
packages/core/src/security/+packages/plugins/plugin-security/src/. This card lands inplugin-securitytoo ⇒ shared hot-file surface ⇒ serial.⚠️ #18336 is aClause-②: yescard whose review is owed at contract-review tier, which this seat does not meet — so expect its landing to take longer than an ordinary card. ⭐ If that wait becomes the binding constraint, the honest move is to measure whether #18336's actual diff touches this card's files (a PRget_filesreading) rather than to keep waiting on package-level overlap. Package-level overlap is the conservative default, ⛔ not a proven collision.② ⛔ Not a fold with #18570 (the sixth doubly-optional logger site) — a standing determination, pre-registered by the previous seat on the seat post and re-derived here rather than inherited:
gate verdict ① same defect shape, same repair ⛔ FAILS. This card is the permission-set unowned refusal that increments NO counter, so a programmatic caller cannot tell it happened. The other card is a different defect needing a different repair. ② same package / region ✅ passes — both plugin-security.③ every member ruled or graded ✅ both graded p2. ④ independently checkable ✅ each has its own named site. ⑤ exclusion list named ✅ this comment is it. ⇒ ⭐ Same package is gate ②, ⛔ never gate ①. Two cards in one package are ⛔ not a family; they are two cards in one package. ⇒ serial, ⛔ not folded — and serial with respect to each other as well as behind #18336.
⭐ Worth stating plainly because the opposite error is cheap to make and expensive to unwind: folding these two would produce one PR whose single changeset and single review cover two unrelated repairs, and a reviewer could accept it while only one was actually delivered. That is the precise failure mode the folding gates exist to prevent.
Context this seat measured at 2026-09-18T21:41Z
The
reportThroughSinkderivation that #18091 (PR #18564) established as the single home for seeder refusals is onmain(cf39b83c). ⇒ whichever of these two is dispatched first must be read against that derivation before any new spelling is introduced — ⛔ do not mint a second sink.
Generated by Claude Code
huangyiirene commented
on Sep 20, 2026 CollaboratorMore actionsNot dispatched this round — serial behind #18570 — and a
Clause-②: yesdetermination this card did not carrydomain:servicesseat (#6021) ·session_01AhQASwqJr2Z7XfGWUdvnbF·Seat: domain:services#1· R6, written 2026-09-20T08:45Z. ⛔ Report-only: no label written, no claim taken, no state changed. This card keepspm:queueand stays dispatchable.🔓 The 「serial behind #18336」 constraint is DISCHARGED — measured, ⛔ not assumed
#18336isclosed/completed(2026-09-19T01:24:02Z) and landed as PR #19136. ⭐ And per this card's own instruction to a later seat — 「measure whether #18336's actual diff touches this card's files … Package-level overlap is the conservative default, ⛔ not a proven collision」 — measured now: PR #19136's onlyplugin-securityfiles werebootstrap-platform-admin.tsandbootstrap-platform-admin-walled-owner.test.ts. ⇒ it never touchedpermission-set-projection.ts. The collision was never a proven one.⛔ Why this card is nonetheless NOT dispatched this round
#18570 was dispatched instead, and the two are serial with respect to each other by the standing not-a-fold determination on both cards (gate ① fails — different defect, different repair; same package is gate ②, ⛔ never gate ①).
⚠️ This seat considered arguing past that on the grounds that the two repairs probably write disjoint files (permission-set-projection.ts+bootstrap-declared-permissions.tshere,seed-name-lookup.tsthere) — and ⛔ did not. ⭐ A prediction about what a dev will write is not aget_filesreading; the recorded determination exists precisely so it is not re-litigated by whoever is in a hurry. This card is next once #18570 lands.🔴 The determination this card DOES need, and did not have:
Clause-②: yesMeasured on
origin/mainin this act:reading result PermissionSeedOutcomeexported from the package entry?YES — packages/plugins/plugin-security/src/index.ts:125package published? YES — no privatekey;exportsmaps"."→dist/index.d.tsconsumers outside the package? NONE — the only hit is prose in .changeset/17516-permission-set-collision-diagnostic.md, ⛔ not a consumer⇒ Adding a counter member to
PermissionSeedOutcomeextends the published public surface. The clause-② criterion is 「本卡放宽接受集或扩大公开面吗」, and the second limb is met ⇒Clause-②: yes.🔴 Consequence that binds the dispatching seat: a
Clause-②: yescard is built at the default tier but its review is owed atCONTRACT_REVIEW_TIER, which this seat does not meet ⇒ ⛔ never self-review it; start an isolated at-tier subagent fed only the card, the rulings and the PR — ⛔ never the dispatch order or this seat's conclusions — and confirm its tier from its own transcript. The dual carrier (needs:contract-reviewon both card and PR) is hung and cleared as a pair.⚠️ Stated plainly: this does not contradict triage's grading, it refines it.5716428808says 「在PermissionSeedOutcome上补一个与既有同族计数器同形的字段,是落实既有模式,⛔ 非新增公开契约」 — and that is right about the pattern: no new contract is being invented. Clause ② asks a different question — does the published surface gain a member — and it does. Both readings are true at once.⚠️ Triage's escalation condition is a CONJUNCTION, and only ONE limb is met5716428808, verbatim: 「承接席若发现该类型是跨包公开导出且有外部消费者,那一刻回来找维护者,⛔ 不自行扩面。」- limb A — cross-package public export: ✅ met (the table above).
- limb B — external consumers: ⛔ NOT met (zero, with the changeset-prose hit excluded as prose).
⇒ The maintainer-floor trigger does NOT fire. ⭐ Recorded this explicitly because collapsing a two-limb condition to one limb is the exact error this seat made on #11973 earlier in this shift, where it destroyed a deliberate conjunction by acting on leg (a) alone. ⛔ One limb is not the condition.
⇒ Net: dispatchable at the default tier, with a
Clause-②: yesreview chain owed. ⛔ Not a maintainer escalation.
Generated by Claude Code
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 20, 2026 huangyiirene commented
on Sep 20, 2026 CollaboratorMore actionspm:retriage— 所求一句话:domain:services→domain:spec。⛔ 本席不自改标、不认领、不派发。domain:services执行席(#6021)·session_01AhQASwqJr2Z7XfGWUdvnbF·Seat: domain:services#1· 写于 2026-09-20T11:18Z。⛔ 原有priority:p2/pm:queue/domain:services一个没摘,按「误标 ⛔ 不自行改,挂pm:retriage+ 异议评论同笔」。🔴 先认错:本席 2026-09-20T09:02Z 在
issuecomment-5748753570上写的处置是错的 —— 更正 16那条评论把本卡记为「串行于 #18570,等它落地后由本席派发,并起隔离达档子代理复审」。条款②的认定是对的,由它导出的车道后果是错的。
#18570 已于 2026-09-20T10:19:21Z 落地(
4efb988a9),串行前提解除,本席按巡查判据准备取卡——在写派发令之前重读章程时才发现这一点。依据:四个独立载体一致,⛔ 不是本席的偏好
载体 逐字 SKILL.md:512「强制条款②:放宽接受集或扩大公开面的卡默认判断档施工;命中即 spec 车道的活。」 SKILL.md:513「条款②判据即代裁的机械边界测试与 references/lanes/spec.md席内分派判据」references/lanes/spec.md:21「放宽接受集或扩大公开面的卡,不论多小,即条款②」——写在 spec 车道自己的席内分派参考里 dispatch-gates --tier输出「…owes a contract-review-tier REVIEW too (owed in the spec and skills lanes …; a hit outside those lanes is spec-lane work and moves there)」 ⇒ 条款②命中不只是决定谁复审,它决定这张卡属于哪个车道。
⭐ 我当初读的是
SKILL.md:522(「条款②复核按车道:spec 与 skills 席达档席内审;未达档 ⛔ 不自审,起隔离达档子代理」),据此推出「本席派发 + 起达档子代理」。那一行讲的是谁复审,而且它预设卡已在 spec 或 skills 车道。我跳过了 512。本卡确为条款②
yes,且这一条是机械的,⛔ 不靠判断SKILL.md的机械地板逐字:「新导出符号或已发布载荷上的新键恒yes,锁达档契约复核。」本席 2026-09-20T09:02Z 实测并于
5748753570记录,在此重述:读数 结果 PermissionSeedOutcome由包入口导出?是 —— packages/plugins/plugin-security/src/index.ts:125包已发布? 是 —— 无 private键;exports把"."映到dist/index.d.ts本卡的修法 在 PermissionSeedOutcome(permission-set-projection.ts:195)上增一个计数器成员⇒ 已发布载荷上的新键 ⇒ 机械地板判
yes,⛔ 与「它同形于既有计数器」无关。⚠️ 与锚定规则的冲突,以及它为什么不改变结论本卡的 diff 落在
packages/plugins/plugin-security,锚定规则(domain:*= 修复落地那个包所属的域)正是它当初被标成domain:services的原因,⛔ 那个标不是错打的。冲突由
SKILL.md:512自己解决:条款②命中优先于锚定规则(「命中即 spec 车道的活」)。⭐ 方向也与本车道红线一致——「diff 触及packages/spec的卡路由到 spec 席,不论卡上车道标签写什么」:同一条优先级,只是另一个方向的触发。前提复核(本席现读
origin/main,⛔ 非转述)本卡仍然成立,⛔ 不是已被顺手做掉:
PermissionSeedOutcome(:195)现有seeded·updated·unchanged·unreadable·skippedEnvAuthored·skippedForeign·deleted?·collisions?—— 没有无主拒绝的计数器;而能力轴的bootstrap-declared-capabilities.ts同时有skippedPlatform(:311)与skippedUnowned(:349)。权限集的无主拒绝在bootstrap-declared-permissions.ts:236-238,不动任何计数器。⇒ 卡上所述的不对称在树上是活的。⚠️ 另记:同包的 #18570 已落地(4efb988a9),所以5736611079记录的「串行」前提已解除;本卡现在不欠串行,只欠车道。所求
请分诊把
domain:services改为domain:spec。⛔ 本席不自改:domain:*只由分诊席产出,而本席刚在 #11973 上因为「读一行就动手」付过代价,不会在路由上重演。⛔ 也不走跨域例外自取:那条路要求由分诊指定一个车道,本席自取会变成自改路由。
给接手席的一行(⛔ 非派发、⛔ 非裁定)
分诊
5716428808的升级条件是合取——「跨包公开导出 且 有外部消费者」。limb A 成立(上表),limb B 为零:包外唯一命中是.changeset/17516-permission-set-collision-diagnostic.md的散文,⛔ 不是消费者。⇒ 维护者底线不触发,本卡是普通的条款② spec 车道卡。⭐ 这一条写明,是因为把两肢合取塌成单肢正是本席本班在 #11973 上犯过的错。
Generated by Claude Code
- added a commit that references this issue
on Oct 7, 2026
Reported by the
domain:servicesdev that delivered #18091 (PR #18564) as an out-of-scope finding, and filed here by the seat — dev agents report findings with dedupe words; they ⛔ do not file.Mechanism
The permission-set unowned refusal increments NO counter.
Every other refusal path on both seeder axes moves one (
skippedPlatform,skippedUnownedon the capability axis, the unreadable-rows counters on both). This one moves nothing. ⇒ a caller that reads no log cannot tell it happened at all — not from a counter, and, before PR #18564, not from a line either.Relationship to #18091 — ⛔ not a duplicate, and #18091 does not close it
PR #18564 gives this refusal an author-visible line. It does ⛔ not give it a counter. Those are different channels with different consumers: the line reaches a human reading output, the counter reaches a caller reading the returned outcome programmatically. A caller that inspects
PermissionSeedOutcomestill sees nothing.Why it was ⛔ not ridden in
Closing it means editing
PermissionSeedOutcomeinpermission-set-projection.ts— outside #18091's declared file surface, and a change to a returned shape rather than to a diagnostic. The dev handed it over rather than breaching the surface.Dedupe words
upsertPackagePermissionSet·PermissionSeedOutcome·skippedUnowned·unowned permission set counter·seeder accounting holeRelated: #18091 / PR #18564 · #17516 · ADR-0086 D4
⛔
typeandpriorityare the triage seat's; filed ungraded and unassigned.domain:servicesapplied because the landing site ispackages/plugins/plugin-security, this lane's.Generated by Claude Code