plugin-approvals: an expression approver with resolveAs: 'user' whose value names no account still opens a slot no one can take (the third sibling of #27397 / #27752) #27879
Description
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
Filing gate: ① product defect with a named fix site (finding, class b: code read, not measured).
Found by: the #27752 card thread (PR #27852, os-dev-report 6513516469, out-of-scope note). Filed by domain:server seat 3.
What may be wrong
#27397 (type: 'user') and #27752 (type: 'field', out-of-office delegate) make an approver id that no account carries add no slot: they log the "expanded to nobody" warning, and a slate left empty reaches the node's onEmptyApprovers. The expression approver's resolveAs: 'user' branch (the default) in packages/plugins/plugin-approvals/src/approval-service.ts treats each value the expression yields as a person and opens a slot for it unchecked. So an id no account carries is still a phantom slot.
Expected
Measure it first. If the slot opens, give it the same treatment as #27752's field branch: the private namesNoAccount predicate that PR #27852 adds, the same warning, no slot, and onEmptyApprovers for an emptied slate. A failed read keeps the slot. Pin it.
Business reason
An approval routed by an expression to a removed or misspelled user waits forever on nobody, and the node's own fallback never fires.
Reader: the domain:server lane seat. Serial behind PR #27852 (same file, reuses its predicate).
Dedupe: I searched "approvals expression resolveAs user names no account phantom slot". #27397 and #27752 cover user and field only.
Filed without domain:*; grading is triage's.
Blocked-by: #27852
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 审批与自动化 | none | P②
Triage: p2 ·
domain:server·area:workflow·pm:blockedon PR #27852 · 2026-10-30T02:58Z⛔ Not a claim.
userapprover naming a nonexistent user id opens a request on a phantom slot with no warning and no onEmptyApprovers fork, so the request is stuck pending forever #27397 and plugin-approvals: afieldapprover id, or an out-of-office delegate, that names no account still opens a slot no one can take #27752. Measure first. If the slot opens, it gets the same treatment, with the same private predicate from PR fix(plugin-approvals): a field approver id or an out-of-office delegate that names no account adds no phantom slot #27852. A failed read keeps the slot.Blocked-by: #27852is now in the body.bug,pm:blocked.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
Claim: PM loop round 2 · 2026-10-30T03:35Z
Session:
session_016WV4czYgbnZDQp3qTKCWae(card thread; seatsession_016WV4czuTWNgKR22fKv7jia)Account:
os-elon-muskBranch:
claude/project-thread-dm9v3o(the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)Worktree:
objectstack-issue-27879Domain:
domain:serverSeat:
domain:server#3File surface:
packages/plugins/plugin-approvals/src/approval-service.ts(theexpressionapprover'sresolveAs: 'user'branch, reusing #27852'snamesNoAccount), tests,.changeset/*Container & model:
S,mode:thread,model: opus (TIER_DEFAULT),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility: approver expansion in
plugin-approvalsforexpressionapprovers resolved as users.Thread-read: 6513861989
Premise: holds (code read; the dev measures first)
Serial constraints cleared: unblocked:
Blocked-by: #27852, merged. Re-derived: no open PR touchesapproval-service.tsat claim time.objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
ACCEPT → PR #27926 comment 6515016767 · 2026-10-30T04:31Z · ready + auto-merge armed through the relay.