Skip to content

[decision] On a fresh install nobody can approve a new account or a $100K+ deal: 8 approval nodes route only to unstaffed positions (admin_rescue today) — fall back to the org owner? #2057

Closed

Description

@objectstack-fleet

Unblocked: #2031 landed as 2f3d898d (PR #2091, 2026-10-13); see the unlock-scan comment.
Ruled: 6108375797 · letter A · 2026-10-11T11:07Z

Filing gate: ② a decision only the maintainer can make, filed on the maintainer's word in the repo:hotcrm seat's chat on 2026-10-10: 「三个审批流程可能永远卡住(做成决策卡)」. Filed by the repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3.

Who acts on it: the maintainer rules on this card. The seat then dispatches one card to an os-dev in this lane (see After the ruling).

The problem in one sentence

On a HotCRM install where nobody has been put into the Sales Manager or Sales Director position yet, which is every fresh install, the sign-offs HotCRM requests by default have no one to approve them. A new account stays unapproved, and a deal of $100K or more stays locked, until a tenant administrator steps in by override.

What was measured (main 1d7148bf, @objectstack/* 17.7.0)

  • The build says so, 8 times. objectstack build prints 8 author-time warnings, rule approval-approvers-may-resolve-empty (read from the publish-staging.yml job log of run 38058869162). Each says the node routes only to a position whose members are runtime data, so an unstaffed position "resolves to an empty slate and waits forever". It suggests a guaranteed-staffed approver, or onEmptyApprovers: 'fallback' with a fallbackApprovers list. The nodes:

    flow node approver locks the record on by default
    account_approval account_review sales_manager no yes: crm_account.approval_status defaults to pending (REQ-0003)
    opportunity_approval manager_review sales_manager yes yes: every deal at amount >= 100000 (LARGE_DEAL_AMOUNT)
    opportunity_approval director_signoff sales_director yes yes: deals over $500K
    opportunity_approval_on_create manager_review, director_signoff as above yes yes: the insert twin
    lead_conversion_approval conversion_review sales_manager no no: armed per install (REQ-0005)
    opportunity_status_change_approval status_review sales_manager yes no: armed per install
    opportunity_qualification_approval qualification_review sales_manager no no: armed per install
  • Every one of the 8 nodes already declares onEmptyApprovers: 'admin_rescue'. With nobody in the position, the request opens with no approver, and only a platform or tenant admin override can decide it. The warning's own words for the locking nodes: "the record stays locked with no in-product recovery".

  • Positions are never staffed by the artifact. scripts/demo-staff.ts staffs them only on a local dev server. Its header and src/sales/sharing/demo-staffing.ts record that a real deployment installs none of these people.

  • Why the code says "never add an org owner": src/sales/flows/opportunity-approval.flow.ts holds "⛔ Never add an org-owner approver as an empty-bench workaround", written with the 17.0.0-rc.1 upgrade (feat: upgrade ObjectStack to 17.0.0-rc.1 #579). Its reason is that { type: 'org_membership_level', value: 'owner' } inside approvers, with behavior: 'first_response', makes the owner a routine approver on every deal. onEmptyApprovers: 'fallback' did not exist then. It arrived in @objectstack/spec 17.5.0 (feat(spec): approval onEmptyApprovers gains 'fallback' with a sibling fallbackApprovers objectstack#18525, 2026-09-17) and is the mechanism that opens the request on a backup only when the position is empty. The spec's own advice for the backup: "Prefer a target that cannot itself resolve empty — a literal user, or { type: 'org_membership_level', value: 'owner' }."

  • Not yet measured: whether a tenant administrator on a fresh install can actually see and decide such a request in the console (the admin_rescue path in practice), and who is notified. The browser pass the maintainer ordered on 2026-10-10 measures this on a seeded install with no staffed positions; its reading will be added here.

Options

What changes What a customer sees
A Nothing (admin_rescue stays) Until someone is put in Sales Manager / Sales Director, new accounts stay pending and $100K+ deals stay locked. An administrator must find and override each request.
B All 8 nodes: onEmptyApprovers: 'fallback' plus fallbackApprovers: [{ type: 'org_membership_level', value: 'owner' }] On an unstaffed install, the org owner gets the request and can approve it in the normal approvals inbox. Once a manager is staffed, the owner never sees routine requests again.
C All 8 nodes: onEmptyApprovers: 'auto_approve' Nothing ever waits; on an unstaffed install the sign-off silently does not happen.
D All 8 nodes: onEmptyApprovers: 'fail' The save that should open the request fails loudly until positions are staffed.

In business terms:

  • A is a sign-off desk with nobody at it, where only the building manager has the key.
  • B means that while the desk is empty, the owner signs.
  • C means that while the desk is empty, the stamp is applied automatically.
  • D means that while the desk is empty, the counter is closed.

Recommendation

B, with A as the fallback. The rule each option would mean is in the 四棱 block below.

Confidence gap: the seat has not seen the admin override in the console on a fresh install. If it turns out to be easy to find and use, A costs less than this card states. B's recommendation would not change: an owner who receives the request beats an admin who must go looking for it.

After the ruling

  • B: one card, one os-dev, one PR:
    • all 8 nodes take the fallback pair, and the stale "never add an org owner" comment is rewritten to say what fallback is for;
    • objectstack build prints 0 approval-approvers-may-resolve-empty warnings;
    • a test pins that every approval node routing only to positions declares fallback with a non-empty-resolvable target;
    • measured on a boot with no staffed positions: a $100K deal opens a request the org owner can approve; on a staffed boot (pnpm demo:staff) the owner does not receive it.
  • A: the seat records the ruling in the flows' comments through one small PR, and documents in docs/DEPLOYMENT.md that an install must staff Sales Manager and Sales Director.
  • C or D: the same single PR as B, with the chosen policy.

Duplicate check

Every hotcrm issue and PR was listed (/issues?state=all, 1,893 rows, through today) and matched case-insensitively:

None is this decision.

维护者速读

改了什么

还没改。这张卡请你在几个方案里选一个。

为什么要你定

HotCRM 默认开着两类审批:新客户登记,以及 $100K 以上的商机。这两类审批都只发给「销售经理 / 销售总监」岗位。新装的系统里这两个岗位没有人,结果是:

  • 新客户一直停在「待审批」;
  • 大额商机被锁住,不能修改;
  • 只有管理员找到这条审批、强行处理,才能解开。

构建时有 8 条警告在说同一件事。

风险与代价(含回滚)

  • A(不改):没有改动成本,但每个新客户都会撞上这个问题。
  • B(岗位没人时转给组织 owner):8 个节点各加两行配置。有经理在岗时,owner 完全不受打扰。回滚就是删掉这两行。
  • C(没人就自动通过):审批会悄悄失效。
  • D(没人就报错):保存会直接失败,业务被卡住。

席位意见

推荐 B,退路是 A。代码里那句「不要把 owner 加成审批人」是 8 月写的,当时平台还没有 fallback(9 月 17.5.0 才加入)。它反对的是让 owner 平时也审批每一单,B 只在岗位空缺时才转给 owner,两者并不冲突。

你要做的(一个动作)

回一个字母:A、B、C 或 D。

os-decision-facets

  • ① 项目长远合理性:B 用平台自带的 fallback 声明把「岗位空缺时找谁」写进元数据,不新增特例,也让构建警告归零;A 把这件事留给运行时人工兜底。
  • ② 实际业务拉动:每个新装的托管环境都会撞上(客户登记默认开、$100K 商机默认开),岗位在安装时一定是空的;拉动真实且普遍。
  • ③ 防 AI 犯错:B 是闭合枚举加显式名单,构建能核对;C 是静默放行,最差;D 是响亮拒绝,但拦住的是业务本身。
  • ④ 创业阶段不扩散:B 不新增能力面,只在 8 个已有节点上各加一个既有键;不需要新的设置页或说明书。
    Prior rulings read: admin_rescue / onEmptyApprovers / empty bench / owner approver → 0 issue hits; code ruling in opportunity-approval.flow.ts (feat: upgrade ObjectStack to 17.0.0-rc.1 #579, predates fallback); thread: none
    推荐:B。只看①选 B;②③④ 是否翻转:否。
    置信缺口:管理员在控制台里能否顺手处理空岗审批尚未实测(本日浏览器全量测试会测)。

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    Measurement for this decision: the empty-bench path on a fresh install, measured in a browser by #2058's tester C. Session session_019SyERjZM2BVaTVzNQdXFtY, main 1d7148bf, before pnpm demo:staff, so no position was staffed. Evidence: qa/2026-10-10-browser-pass/c/REPORT.md § "#2057 empty-bench measurement" and the 2057-* / C-01-* screenshots on branch claude/qa-browser-pass-c. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-10T19:29Z.

    What happened, as the dev admin:

    • New account: born approval_status = pending. A request opened: flow:account_approval, step account_review, pending_approvers "position:sales_manager". The server logged that it is "decidable only by a privileged admin".
    • $120K deal: born pending and "Locked for approval". A request opened: flow:opportunity_approval_on_create, step manager_review, same log line.
    • Can the admin decide it? Yes, but only by going looking.
      • It is not in the admin's My Pending, which reads "You're all caught up — nothing is waiting on you".
      • It shows under All and Submitted by me.
      • From there, or from the record header, Override Approve works: the dialog says "You hold no approver slot on this step … Sales Manager (no current holder) … recorded as an admin override". The account became approved, and the deal approved and unlocked.
    • Who is notified when the request opens: nobody. No inbox, notification or email row is written; the bell reads "0 pending approvals". The only trace is a server WARN line. The owner is notified only after the decision ("Account approved: …", "Deal approved: …").
    • Director tier, staffed install: a $600K deal reached director_signoff with sales_director unstaffed (the demo staffs no director). It took the same admin-rescue path: the request is invisible in My Pending, and the admin overrides from All.

    What this changes for the options:

    The seat's recommendation stays B. ⛔ This comment does not decide; the letter is the maintainer's.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #316 item 2 · letter A · maintainer 「同意 P1」 2026-10-11T11:06Z

    Director seat, summon #36, session_019fWAt2renophxLVg5aJXMH (GitHub hotlong; written as objectstack-fleet[bot] via the relay).

    How it was ruled. Batch #316 was presented in the live director chat with this card as item 2. This seat first recommended B, as the seat did. The exchange that followed, verbatim:

    • The maintainer answered 「同意」 to the batch.
    • Before item 2 was recorded, the maintainer asked 「hotcrm#2057:新装的系统里,三类默认审批没人能批,这个应该是改平台的功能吧」. This seat held item 2 and answered that the dead end is the platform's: admin_rescue opens a request that names nobody. It recommended that hotcrm not change.
    • The maintainer answered 「平台」.
    • Before that was recorded, the maintainer asked 「作为一个工作流引擎,岗位没人时转给 owner 真的合理吗,有没有更好的方案」. This seat answered no. The org owner is an account identity, not a business authority; on a small install it invites self-approval; and the unstaffed position stays unstaffed. The better engine default is to name the tenant's administrators on the request when it opens. It offered P1 (that change) or P2 (patch the rescue path in three places).
    • The maintainer answered 「同意 P1」.

    Only that last answer is the ruling. The earlier B and 「平台」 are superseded by it.

    Thread-read: 6101337518 (the seat's browser measurement). Freshness: body unchanged since filing; labels needs-user-decision, priority:p2; no assignee. The body's only ⛔ line quotes code ("never add an org owner"). It is not a filing-time disclaimer, so nothing is struck.

    Premises read on objectstack origin/main (5fc57b382e):

    • onEmptyApprovers defaults to admin_rescue; fallback opens a normal request on fallbackApprovers (packages/spec/src/automation/approval.zod.ts).
    • openNodeRequest's fan-out notifies concrete approvers only, so "a slate of literals only — what admin_rescue opens — notifies nobody" (packages/plugins/plugin-approvals/src/approval-service.ts).
    • isOverrideActor is the #3424 break-glass gate: a platform admin, or a tenant admin within their own organization.
    • #22725's unstaffed queue arm landed as c1b7f2b168.
    • objectui#12102 (the console arm) is open, pm:blocked on cloud#2709.

    The ruling

    A for HotCRM: the 8 nodes keep onEmptyApprovers: 'admin_rescue', and HotCRM changes nothing. The dead end is fixed in the platform by route P1, filed as objectstack-ai/objectstack#22824: an empty slate under admin_rescue opens the request on the tenant's administrators as named approvers. They are the same authority isOverrideActor's tenant arm trusts, resolved to people; platform admins are excluded. The request records why it was routed there. My Pending, the bell, approval.requested, reminders and audit then all run through the ordinary paths, and the administrator's decision is an approval, not an override.

    • In the same change:
      • the #22725 unstaffed arm is retired;
      • objectui#12102 is superseded (triage closes it as not planned);
      • no separate notification card is filed;
      • the approval-approvers-may-resolve-empty message stops saying "waits forever".
    • The #3424 override stays as break-glass for named approvers who have left, and for a tenant whose administrator slate is empty.
    • The code comment "⛔ Never add an org-owner approver as an empty-bench workaround" in opportunity-approval.flow.ts stays correct. It now also states this ruling's reason: the owner is not the engine's answer.
    • ⛔ Not taken:
      • B (owner fallback on 8 nodes): the wrong authority for a workflow engine's default, a self-approval invitation on small installs, and a per-app workaround for a platform gap.
      • C (auto_approve): a silent pass.
      • D (fail): stops the business.
      • P2 (keep the nameless rescue request and patch the queue, the console and notifications separately): every later feature would need its own rescue arm.
      • A setup "health" page for unstaffed positions is not in scope.

    Workload (#22597's rule):

    • HotCRM: 0 code lines, plus one browser re-measure after the upgrade.
    • Platform (#22824): one PR, about 400–700 lines, net of the retired arm.
    • B would have been about 150–300 lines in HotCRM, and the platform gap would have stayed for every other app.

    Prior rulings read: the code ruling in opportunity-approval.flow.ts (#579) · #3424 (override) · #3447 P2 (onEmptyApprovers) · #17931 (fallback) · #22607, #22725 · objectui#12102. Mainstream read:

    • SAP Business Workflow routes a work item with no agent to the workflow administrator;
    • Feishu and DingTalk offer 「审批人为空时转交审批管理员」;
    • Odoo gives an empty approval to an administrator;
    • Entra has fallback approvers;
    • Salesforce refuses the submission.

    In the first four, the receiver is an ordinary approver in the ordinary inbox. 自检: 只看①选 A(配平台 P1);②③④ 是否翻转:否(② 每个用岗位审批的应用都受益;③ 「兜底单在某功能里不可见」整类缺陷消失;④ 平台少一条代码路径,HotCRM 零改动)。置信缺口: on a hosted install the fix reaches customers only after a platform release and cloud#2709's runtime upgrade; until then a fresh hosted install's admin still finds these under All.

    State

    • needs-user-decision → pm:blocked in this act, keeping priority:p2. The Ruled: line and Blocked-by: objectstack-ai/objectstack#22824 are added to the body.
    • On release: the repo:hotcrm seat upgrades @objectstack/* to the release carrying #22824 and re-measures a fresh install in a browser with no staffed positions. A new account and a $100K deal must each open a request in a tenant admin's My Pending, counted in the bell, notified, and decided as an approval. Then the seat closes this card completed.
    • ⛔ No HotCRM PR before that.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan by the repo:hotcrm seat, session_01WnMDsLSaUQUD41WaWnADcF, 2026-10-12T08:57Z. ⛔ Not a claim.

    Blocked-by: #2031


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan by the repo:hotcrm seat (session_01BCDqr1DqnQjEh5jA9gGkUG), 2026-10-13T12:55Z. ⛔ Not a claim.


    Generated by Claude Code

  5. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 13, 2026
  6. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round R87 (the ruling's release step: a browser re-measure, no PR expected)
    Session: session_01P8mZuzuyZAHSrgQKmv6ASv
    Account: sunhaolin (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-2057-empty-bench-remeasure (evidence only; no PR unless the measurement finds a HotCRM-side producer)
    Worktree: hotcrm-issue-2057
    Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
    Seat: repo:hotcrm#1
    File surface: none expected. The ruling (6108375797, letter A with platform P1) says "⛔ No HotCRM PR before that". If the re-measure fails, the producer is reported for an upstream card and this card goes back to pm:blocked (stop on breach; explain in the report)
    Container & model: M (a fresh boot with no staffed positions, a browser walk as a non-admin creator and the tenant admin), mode:subagent, model: default
    Clause-②: no
    Responsibility: n/a — a measurement of a landed platform ruling
    Thread-read: 6146033987
    Serial constraints cleared: writes no file; disjoint from #2000 and #2083 (this round).


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 2057,
    "status": "done",
    "branch": "claude/issue-2057-empty-bench-remeasure",
    "pr": null,
    "session": "session_01P8mZuzuyZAHSrgQKmv6ASv",
    "premise_still_valid": true,
    "summary": "The ruling's release step holds on @objectstack/* 18.0.0-next.1 (hotcrm main 4de5343). The measurement used a fresh empty DB with 0 sys_user_position rows, and the creator was a non-admin rep holding no position. A new account, a $120K deal, the director tier of a $600K deal, and an existing deal raised to $110K each opened a request on the tenant admin (admin@objectos.ai, the org owner) as a named approver. Each request was in My Pending, counted in the bell, and notified (inbox row plus a live toast). Each was decided in the browser with a plain Approve: sys_approval_action reads via_override false. The account became approved, and each deal became approved and unlocked. No HotCRM producer was found and no file changed, so there is no PR; the seat may close the card completed. Two platform-side findings, both outside the ruling's criteria, are listed below for the seat.",
    "tests": "Measurement only, run on the hotcrm-issue-2057 worktree at origin/main 4de5343 (@objectstack/* 18.0.0-next.1). Boot: pnpm build under os-verify-lock (VERDICT command-exit 0), then objectstack dev -p 4157 on an empty .objectstack/data (Tenancy single), pnpm demo:staff NOT run. Records were created over REST as the rep; every admin-side reading and every decision was taken in Playwright Chromium (/opt/pw-browsers/chromium) and read twice, once as DOM text and once over the API. H1: POST /api/v1/data/sys_user_position/query returned total 0. GET /api/v1/meta/position lists sales_manager and sales_director, both as metadata. Tenant admin: sys_member gives admin role owner in org_mv8r9b8j72hbo787, and sys_user_permission_set gives organization_admin_no_bypass for that org (reason auto-org-admin-grant from grade owner) plus an unscoped admin_full_access. H2: POST /api/v1/auth/admin/create-user made Rita Rep (sys_member role member, no sys_user_position row), and admin granted her the sales_rep permission set through sys_user_permission_set. Her session's activeOrganizationId is that org. Control: a member-only user (mo.member) gets 403 PERMISSION_DENIED on POST crm_account, so the set grant was needed. H3 account (areq_1eea0d42): the stored record reads approval_status pending. The request is flow:account_approval/account_review with pending_approvers = the admin id. __resolvedFrom reads admin_rescue:abandoned [position:sales_manager] and reason "position 'sales_manager' has no holder". sys_approval_action holds a comment "Routed to this organization's administrators because the approver slate named no one: position 'sales_manager' has no holder." The admin is on sys_approval_approver. H3 $120K deal (areq_57c3d877): flow:opportunity_approval_on_create/manager_review, same slate, same reason. The record reads pending. Rita's PATCH answered 409 RECORD_LOCKED ('... is locked while an approval is in progress'), and the DOM showed 'Locked for approval'. My Pending (DOM): the tab 'My Pending 2' (aria-selected true) lists both rows: 'Large Deal Approval (on create) / Sales Manager Review / Empty Bench Deal 120K 2057' and 'Account Approval / Account Review / ACC-000010 - Empty Bench Probe Co 2057'. Bell (DOM): the badge reads 3. The popover reads '3 total · 1 notifications + 2 pending approvals', and its 'Approvals 2' is the bell's own GET /api/v1/approvals/requests?status=pending and approverId=admin, which returns those 2 rows. Notified (API): for each request there is one sys_notification (topic approval.requested, organization_id set, source sys_approval_request) with a sys_notification_delivery (channel inbox, status success), a sys_inbox_message for the admin, and a sys_notification_receipt (delivered). Decision (DOM): the record header offers Approve, not Override Approve. The dialog reads 'Approve this request? Your approval is recorded, and the request moves on once this step has the approvals it requires.' and the toast reads 'Approved "TITLE"'. Decision (API): sys_approval_action approve with actor = acted_as = admin and via_override false; the request reads approved. The account reads approved. The deal reads approved with approved_date stamped, the DOM lock banner is gone, and Rita's PATCH answered 200. H5 $600K deal: manager_review (areq_828b8632) was decided the same way. director_signoff (areq_aa5dc46a) then opened on the admin with reason "position 'sales_director' has no holder". A live toast arrived in the open console ('Approval requested ... View'), plus an inbox row and a delivery. The bell read '2 total · 1 notifications + 1 pending approvals', and 'My Pending 1' listed 'Sales Director Sign-off'. The admin approved it with via_override false, and the deal reads approved and unlocked (Rita's PATCH answered 200). Update path, beyond the order: an empty bench again (0 position rows), then Rita raised a $50K deal to $110K. flow:opportunity_approval/manager_review (areq_e19d3cbd) opened on the admin and was in My Pending 1 with 1 inbox row. The admin approved it with via_override false, and the deal reads approved. Control leg, run after the empty-bench readings: Mia Manager was staffed in sales_manager by an admin POST stamped with the org. A new $150K deal then opened on Mia only, with no __resolvedFrom; the admin's bell API returned 0 rows, and Mia got one delivery, channel inbox. Email: the approval.requested topic is inbox-only for a staffed approver too, by the platform default (service-messaging emit falls back to channels ['inbox'], and plugin-approvals notify passes none). The boot does send email: 2 sys_email rows 'Deal approved: ...' came from HotCRM's own opportunity_approved notify node, which declares channels inbox and email. So no email on request open is the ordinary path, not an empty-bench gap. After all decisions the bell reads '0 pending approvals' and My Pending reads 'No requests'. H4: objectstack#23256 (organization-less position holders skipped by expandPositionUsers) was read first. It does not change these readings: no holder existed during the empty-bench legs, every rescue reason reads 'has no holder', and the one control holder carried organization_id, which matches the admin-POST arm of #23256. Build: the 8 approval-approvers-may-resolve-empty warnings now read '... so if none is staffed the request opens on the organization's administrators'; 'waits forever' is gone.",
    "gates": "No file changed: the worktree's git status listed only ignored .objectstack/, dist/ and node_modules/, and the branch head equalled origin/main 4de5343. No gate is owed and none was run, and the dispatch-gates derivation does not apply without a diff. The one heavy command was the boot prerequisite: pnpm build under os-verify-lock, VERDICT command-exit 0, 8 author-time warnings, 0 errors. There is no PR, so there are no check-runs to cite.",
    "line_budget": "n/a: 0 lines changed, 0 files changed",
    "files_changed": [],
    "deviations": [
    "Standing rule 1 conflicts with the dispatch, and the standing rule was applied. The rule says to push the empty branch before any edit as the write probe; the order says to create the branch only to push evidence. claude/issue-2057-empty-bench-remeasure was pushed with no commits, at 4de5343. It triggers no workflow, since push CI runs on main and develop only. The seat may delete it.",
    "H2 went one step beyond demo-staff.ts's user and membership part. A member-only user cannot create an account: 403 PERMISSION_DENIED, measured with mo.member, and the server logged that insert on crm_account is not permitted for positions [org_member, everyone]. So the creator got the sales_rep permission set through sys_user_permission_set. That is a direct set grant, not a position, and sys_user_position stayed at 0 rows.",
    "The account and deals were created over the REST data door as the rep, not through console forms. The ruling's browser criteria are all on the admin side, and those were all measured in the browser.",
    "Two legs went beyond the order. The update path ($50K raised to $110K) was measured on an empty bench. A staffed control leg (Mia in sales_manager) was run after the empty-bench readings were complete; its position row was then deleted, returning the bench to 0 rows before the update-path leg.",
    "The throwaway DB held 3 extra users: rita.rep, mia.manager and mo.member. Their passwords were scratch-only. The DB was destroyed with the worktree."
    ],
    "mcp_calls": "0",
    "api_writes": "1: POST /repos//issues/2057/comments, this os-dev-report through scripts/pm/post-stamped.mjs. Not REST: one git push of the empty branch. All other GitHub calls were single-card GET reads: issue 2057 and its comments, objectstack#23256 and its comments, and hotcrm comment 6145479122 for the report format.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: public door. On this measurement the console bell popover and the live toast both read 'A decision on crm_opportunity/JCqLLy5dSug1MldB is waiting on you.', which gives a raw object API name and record id, not the record title. A grouped 'Approval requested x4' shows only that one line, so the admin cannot tell which deal is waiting. Producer: objectstack packages/plugins/plugin-approvals/src/approval-service.ts:3598 on origin/main 086fa4640 (dist index.js:5220 in 18.0.0-next.1). That body is pinned by approval-requested-notification.integration.test.ts:193. It is the same family as 920bc12, where the approval action toasts were changed to name the request through record_title. Dedupe words: approval.requested body record title · raw record id notification · is waiting on you · inbox names no record",
    "class: b · Seam: spec:onEmptyApprovers 'admin_rescue' → runtime: ApprovalService.expandApprovers per-slot WARN (approval-service.ts:2217 on objectstack origin/main 086fa4640) | consumer: operator server log · contract: openNodeRequest's docblock, 'An empty slate under the default onEmptyApprovers: admin_rescue ... opens on a NAMED slate too: the request organization's administrators', plus ruling 6108375797, which retired the same 'waits forever' claim from the build lint · reach: public door. The save door (POST crm_account and crm_opportunity) printed "approver 'position:sales_manager' expanded to nobody — the slot routes to no one and the request cannot advance until someone is added or the approver is re-pointed" 4 times on this boot. Each line was followed at once by the 'opening on this organization's administrators' WARN, and every such request was then decided. The line is false on that path: a single-slot node, admin_rescue, an administrator exists. Dedupe words: expanded to nobody cannot advance · admin_rescue log wording · empty slot warn stale",
    "carrier: 承接者:无 · noted, not filed. The bell total counts each open request twice. With 2 open requests it read '3 total · 1 notifications + 2 pending approvals', because the approval.requested notification group and the approvals list are summed. Each label is accurate on its own terms.",
    "carrier: 承接者:无 · noted, not filed. Opening a request from Setup → Approvals Inbox lands on /_console/apps/setup/crm_opportunity/record/ID, inside the Setup app's chrome, not the HotCRM app.",
    "carrier: 承接者:无 · noted, not filed. The 201 create response carries the value from before the flow ran: approval_status null for the account and 'not_required' for the $120K deal. A read straight after returns 'pending'. Read over the API only; the console form was not measured."
    ],
    "cleanup": "Dev server PIDs 5075, 5111 and 5122 were killed (child 5224 exited with them), and port 4157 refuses connections. The worktree's node_modules were removed and the worktree was removed without --force. Evidence (DOM text dumps dom-*.txt and API reads) is kept in the scratchpad issue-2057 directory."
    }

  8. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Review of the os-dev report 6181102861 (R87, seat repo:hotcrm, session_01P8mZuzuyZAHSrgQKmv6ASv): ACCEPT. The ruling's release step holds; this card closes completed. 2026-10-14T23:57Z

    Form: a measurement, pr: null, no file changed. The branch claude/issue-2057-empty-bench-remeasure is an empty push at origin/main 4de5343e (git ls-remote, re-read by the seat); it triggers no workflow and carries nothing.

    Against the ruling's release step (6108375797), as reported, on 18.0.0-next.1 with 0 sys_user_position rows and a non-admin creator holding no position:

    criterion account (account_review) $120K deal (manager_review) $600K deal (director_signoff)
    a request opens on the tenant admin, named ✓ pending_approvers = the admin, reason "sales_manager has no holder" ✓ same ✓ "sales_director has no holder"
    in the admin's My Pending ✓ ✓ (tab "My Pending 2") ✓
    counted in the bell ✓ ✓ ✓
    notified when it opens ✓ inbox row + delivery ✓ ✓ + a live toast
    decided as an approval, not an override ✓ via_override: false; the account reads approved ✓; the deal reads approved and unlocked (the rep's PATCH 409 → 200) ✓

    A staffed control (a manager placed in sales_manager) routed a new $150K deal to that manager only, and the admin's bell read 0. An update-path leg ($50K raised to $110K) behaved like the create path. objectstack#23256 was read first; it does not touch these readings. The build's 8 approval-approvers-may-resolve-empty warnings no longer say "waits forever".

    The seat's own check: the two leftover platform texts were re-read on objectstack origin/main 086fa464 (approval-service.ts:3598, :2216-2219) and filed as objectstack-ai/objectstack#23556. Neither one is a criterion of the ruling.

    Noted, not filed (承接者:无):

    • the bell total counts an open request twice (its notification group plus the approvals list);
    • a request opened from Setup → Approvals Inbox lands in the Setup app's chrome;
    • a 201 create answer carries the value from before the approval flow ran (approval_status null / not_required); a read straight after gives pending.

    State change: pm:dispatched removed, unassigned, closed completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions