Repository navigation
docs(adr): ADR-0142 D2 tells an empty queue expansion from a failed member read; the D6 slate and the D7 grant pass refuse a failed read - #29865
Conversation
…ember read; the D6 slate and the D7 grant pass refuse a failed read An empty expansion stays a fact the consumers each decide; a failed member-table read is a fault. The admission consumers (the access path, the claim admission, the assignment pool) keep "nobody" because for them nobody denies. The approver slate and the sharing-recipient grant pass refuse it: the slate is held as the queue literal with a warning that names the failed read, and the grant pass stops before any revoke and keeps the rule's existing grants. Status line carries the second amendment note. Text only: no code, schema or changeset moves here. Claude-Session: https://claude.ai/code/session_01JQwxbYJgdcYRwx5iQa5Ksc Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS Contract-review-claim: session_01JQwxbacSXJQqAztgt4UMrS · head 2f923ae · 2026-11-03T08:43Z |
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS Contract reviewServed-tier: Read: card #29738 (body, comments 6594482347, 6597517535, 6599161425), parent #29604 (body, comments 6588472222, 6589473271, 6591670782, 6594432222 — the ruling, letter B), PR #29865 body and file list, the net diff against ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
What holds.
Must-fix 1 — D6 note: two sentences are false against Must-fix 2 — D2 note: the fault list omits the Should-fix 1 — D2: "a thrown failure naming the specification and the table" — the Should-fix 2 — D6: item 2 spells the literal Implemented-by: VERDICT: FAIL |
…ctory read; D2 names every read the expansion makes Round 2 of the contract review. D6: on a failed read behind a queue, team or position approver the approval node refuses to open under every onEmptyApprovers policy, loudly, with its own retryable error class naming the failed read; the execution point is named (the resolver's directoryFailure, the queue branch's early return, openNodeRequest before slateNamesAPerson). D2: the fault list covers the sys_queue row read, so a queue that could not be resolved is not a queue that resolves nowhere; a refusal names the read. Item 2 spells the never-written literal by the queue's machine name. Status line cites both ruling records. Claude-Session: https://claude.ai/code/session_01JQwxbYJgdcYRwx5iQa5Ksc Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS Contract-review-claim: session_01JQwxbacSXJQqAztgt4UMrS · head 589be30 · 2026-11-03T09:51Z |
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS Contract reviewServed-tier: Round 2 of review 6599389641 (FAIL on ① Derived judgments
② Semver level
③ Boundary flagsRound-1 findings, each against the new head:
The dev's addendum 6600440519: ④ Design judgment
The D6 note states ruling B — phrase by phrase. Trigger: "A failed read behind the queue (D2) refuses the slate, and the approval node then refuses to open" ↔ "when a directory read behind a queue, team or position approver fails, the approval node refuses to open". Policies: "under every Every execution-point anchor exists and says what the ADR says. D2's fault list against every read the expansion makes. Admission consumers (D3/D4/D5) — the bullet is unchanged from round 1 and the sites are unmoved: D5 D7 note — the delta changes one phrase ("a failed read behind the queue (D2)"), and the passes it names still hold as round 1 verified: Should-fix 1 — D2's enumeration names fewer reads than its quantifier. (a) "the member tables a specification reads ( Should-fix 2 — the execution point names the primary slate only. For the code half, not findings: the resolver's Nothing the ADR now promises is false of the tree in a way the code half will not fix; each must-fix of round 1 is closed; the D6 note states ruling B with no wider and no narrower scope. Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS 维护者速读
席位核对ACCEPT ·
|
Requested by Warren · project thread
Fixes #29738
Part of #29604 — step 1 of ruling B (record 6594432222), with the D6 target set by the maintainer's second ruling (record 6599731698, B). The code half stays open on the parent and is dispatched after this PR merges.
domain:server#4 · os-warren · session_01JQwxbYJgdcYRwx5iQa5Ksc
Clause-②: no (ADR text only; no published accept set moves)
Metadata-protocol: none
维护者速读
sys_queue行、成员行和各成员表)分开,并写明谁得哪个答案:三类准入消费者(D3 访问路径、D4 认领、D5 分派池)在读失败时继续答「没人」,因为对它们来说「没人」就是拒绝;D7 共享授权重算在读失败时拒绝并保留原有授权;D6 审批人名单读失败时,审批节点拒绝打开,所有 onEmptyApprovers 策略一律如此,单独的可重试错误类并点名失败的读取。状态行加第二条 Amended 注记,引用两条裁决记录。expandRoleand approvals role-tier recipients read sys_member at a 10,000-row bound and turn a failed read into an empty list, so a large or unreadable membership silently shrinks the set #29362、plugins: the rest of the sharing and approvals recipient reads still truncate at 10,000 rows or turn a failed read into "nobody" (sys_team_member, sys_user_position, holdsRole/teamHasMember, queue expandSpec) — #29362 close-out #29503 之后都已改为读失败即拒绝,队列是最后一个。你在 plugins: a queue sharing recipient still revokes grants on a member-table outage, because the queue expander answers "nobody" for every consumer; refuse for the sharing-grant pass and the approver slate (ADR-0142 amendment first) #29604 的决策卡上选了 B:先改 ADR,再改代码。docs/adr/0142-ownership-queue-membership-claim-and-assignment-rules.md一份文件,按风险:① D6 第 5 条之后新增的 Amended 段(节点拒绝打开一段,含执行点:resolveApproverSpec 的 directoryFailure、queue 分支的提前返回、openNodeRequest 在 slateNamesAPerson 之前拒绝);② D2 新增段的两个要点(读失败清单含sys_queue);③ 第 3 行状态行末尾的新注记。git diff origin/main...HEAD --stat预期只列这一份文件,1 file changed, 61 insertions(+), 3 deletions(-);被改的 3 行是状态行和 D6 第 2 条的两行(字面量改用队列机器名拼写)。Head-sha: 589be3056a8aefc66f6273c9d0cd060d6979d3b4),在本 PR 上点 Approve(docs/adr/**为 Tier H,需你的批准);之后由席位落地。What changes
One file,
docs/adr/0142-ownership-queue-membership-claim-and-assignment-rules.md. Text only: no code, no schema, no changeset (skip-changeset—docs/adr/**publishes nothing).sys_queue, through which an authored queue resolves), the member rows (sys_queue_member), and the member tables a specification reads (sys_user_position,sys_team_member,sys_business_unit_member, the BU tree). A queue that could not be resolved is not a queue that resolves nowhere. A table absent from the composition is absence, not failure. The consumer says which answer it is owed when it calls (how is the code half's): the admission consumers (D3, D4, D5) keep "nobody", logged atwarn, because for them nobody denies; the recompute consumers (D6, D7) refuse, as a thrown failure naming the read, never[],nullorfalse.onEmptyApproverspolicy (admin_rescue,auto_approve,fail,fallback). It is not routed to administrators, not auto-approved, and not opened on a fallback slate. It fails loudly, with its own error class (notNO_APPROVERS, not "expanded to nobody"), names the failed read, and is retryable. There is no parked or auto-retry state. The ruling putsteamandpositionapprovers in the same class. Execution point:resolveApproverSpec'sdirectoryFailure; the queue branch's early return consults it first;openNodeRequestrefuses beforeslateNamesAPersonhands the slate toonEmptyApprovers. Item 2's "never written again" stays whole, and item 2 now spells the literal by the queue's machine name (queue:NAME).evaluateRule,evaluateAllForRecord,revokeRuleGrantsForRetiredRecipients) and the per-user recompute (reconcileRuleForUser, reached fromrecipient-membership-recompute.tsthrough the queue's holder check) each stop that rule's pass before any revoke, keep the rule's existing grants exactly as they were, log atwarnnaming the rule and the read, and let the record's other rules reconcile — the plugins: sharingexpandRoleand approvals role-tier recipients read sys_member at a 10,000-row bound and turn a failed read into an empty list, so a large or unreadable membership silently shrinks the set #29362 isolation shape. A grant is re-evaluated on the rule's next pass. The membership-write look-through (queuesNaming) revokes nothing and is outside this amendment.Nothing else in the record moves: D1, D3, D4, D5, D8, D9, the alternatives, open items, verification notes and execution plan are untouched, apart from D6 item 2's literal spelling, aligned to the queue's machine name. D5 already says a failed read during evaluation leaves the row and warns; the D2 paragraph cites it rather than restating it.
Why
The measure the order asked for, read at
origin/mainc76c0e04(the branch base; the merge to42ff86f6moved none of these files):packages/plugins/plugin-sharing/src/ownership-queue-service.ts:expandSpecL305–318 (catch → warn once →[]),holderCheckL394–404 (catch → warn once →false),authoredMemberSpecsL466–497 (catch → warn once, authored members count as nobody),authoredQueueNamesL439–463 (catch →[]). The module header L33–39 declares that posture; ADR-0142 did not.packages/plugins/plugin-sharing/src/sharing-service.tscallerQueuesL890–909 (queuesOf, catch →[]) andsitsInCallersQueueL925–952 (isMember);packages/plugins/plugin-security/src/kernel-queue-membership.tsresolveOntoL89–113 (queuesOf, catch → fails closed).packages/plugins/plugin-security/src/security-plugin-authz-middleware.tsL1517 (isMemberadmits a member's insert naming a queue); a claim's D3 basis issitsInCallersQueueabove;security-plugin-explain-access.tsL512 (isMemberfor the explain reason).packages/objectql/src/assignment-rules.tsL291–301 (expandMembers;[]parks with the "no members" warning, a throw parks with "members could not be read").packages/plugins/plugin-approvals/src/approval-service.tsexpandQueueUsersL3326–3331 (expandMembers, purposeaddressing), called fromresolveApproverSpecL2621–2627; the queue "expanded to nobody" branch L2674–2690; the graph types' failure class L2712–2718 →warnExpansionFailedL2742–2759;holderReadRefusalL1142.packages/plugins/plugin-sharing/src/sharing-rule-service.tsexpandRecipientqueue branch L2176–2186 (expandMembers; feedsevaluateRuleL1253, the per-record pass L1413–1425 with its per-rule catch,revokeRuleGrantsForRetiredRecipientsL1573–1600) andruleReachesUserL1745–1749 (isMember, the per-user recompute fromrecipient-membership-recompute.tsL300–323 with its per-pair catch).So a
queuesharing recipient is diffed against a set missing its members on an outage and its grants are revoked, and a queue approver routes to a shrunk slate, while every other recipient read refuses since PR #29602 (#29503). The ruling (B, record 6594432222 on #29604) puts the ADR text first.Tests
None: a docs-only diff. No package is touched, so no build closure (①) and no package test (②) is owed; the derived gate families (③) are under Verification.
Acceptance notes
openNodeRequestroutes a literal-only slate throughonEmptyApprovers. The note names the execution point for the code half; the held-literal reading is withdrawn.expandMembersalready takes foraddressing, or a refusing variant) is plugins: a queue sharing recipient still revokes grants on a member-table outage, because the queue expander answers "nobody" for every consumer; refuse for the sharing-grant pass and the approver slate (ADR-0142 amendment first) #29604's to choose; the record fixes who is owed which. The contract review notes that the spec'sQueueExpansionPurpose(access|addressing) is an independent axis; refuse-vs-nobody must not overload it.QUEUE_READ_LIMIT(silent drop past 10,000 rows, comment 6591670782 on plugins: a queue sharing recipient still revokes grants on a member-table outage, because the queue expander answers "nobody" for every consumer; refuse for the sharing-grant pass and the approver slate (ADR-0142 amendment first) #29604) is not an ADR matter and is not mentioned; it stays the code half's call, as the ruling record says..github/PULL_REQUEST_TEMPLATE.md's headings are folded into the os-dev order (What changes / Why / Tests / Acceptance notes / Verification), its two declaration lines sit at the top, and its Human Minimum list follows. The first body line is the platform's attribution marker, kept as the os-dev exception.Co-Authored-Byspelling was not used (reported, not a deviation).Verification
Head
589be305: commit9ea89d0e(round 2, after contract review 6599389641 and ruling 6599731698) merged withorigin/main74fd4bf6. The round-2 gate readings (21 derived commands pluspnpm lint, all exit 0;--ran21/21, 0 NOT-MEASURED) are in the os-dev-report addendum 6600440519 on #29738. The round-1 readings on2f923ae4follow; each exit code was captured before any pipe and recorded as aCMD :: exit Nline.node scripts/pm/dispatch-gates.mjs --commands(no paths; the change set is derived from the merge base): 21 commands. A first derivation on the pre-merge head8c3d7b9cprinted STALE TREE (61 commits behindorigin/main, 10 derivation inputs moved), soorigin/mainwas merged and the list re-derived clean before any gate ran.check:doc-formula-expressions:pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lintunderscripts/pm/os-verify-lock.sh—VERDICT command-exit 0 · held the lock 168s · waited 0s.21 derived gates, all exit 0, plus
pnpm lint(eslint . --no-inline-config) exit 0node scripts/check-adr-links.mjs:: exit 0node scripts/check-adr-links.mjs --self-test:: exit 0node scripts/check-closing-keyword-parity.mjs:: exit 0node scripts/check-closing-keyword-parity.mjs --self-test:: exit 0node scripts/check-comment-mask-corpus.mjs:: exit 0node scripts/ci/plan-test-core-shards.mjs --self-test:: exit 0pnpm --filter @objectstack/lint run check:doc-formula-expressions:: exit 0pnpm check:adr-anchors:: exit 0pnpm check:adr-index:: exit 0pnpm check:adr-symbol-anchors:: exit 0pnpm check:ci-filter-parity:: exit 0pnpm check:closed-ceremony-text:: exit 0pnpm check:cross-package-test-inputs:: exit 0pnpm check:doc-authoring:: exit 0pnpm check:driver-memory-census:: exit 0pnpm check:gitlink-declared:: exit 0pnpm check:nul-bytes:: exit 0pnpm check:pm-governed-merges:: exit 0pnpm check:pm-prior-rulings:: exit 0pnpm check:refd-timer-probe:: exit 0pnpm check:watch-hint-literal:: exit 0pnpm lint:: exit 0node scripts/pm/dispatch-gates.mjs --ran gates-ran.txt:✓ dispatch-gates --ran: 21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED (a DERIVED zero — all 21 recorded an exit code and none of them is 3);pnpm lintis listed as outside this card's derivation (recorded, not an error).--tieranswersModel tier — MANDATORY: claude-fable-5-1fordocs/adr/**; this thread ran at that tier at effort xhigh. Changed lines 64 (+61 / −3) vs the 3,000 human-merge threshold: under.!in the title and no(narrowing)arm.Human Minimum
packages/specholds no runtime logic — not touched.pnpm lintpasses — see Verification.main, one pull request.skip-changeset, both declaration lines above.packages/spectouched — no.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code