Skip to content

Global (objectless) body actions are unreachable — runtime registers them under 'global' but the dispatcher only probes '<object>' and '*' #509

Description

@yinlianghui

Split out of #491 so the platform-level defect is visible on its own and leadership can decide whether to escalate it to the upstream runtime repo. This is an upstream @objectstack/runtime bug, not app metadata — nothing authored in this repo can make a global action dispatch.

What "global actions" are

An Action with no objectName is meant to be available on every object's surfaces — this app shipped two: log_call and log_meeting ("log a call/meeting against whatever record you're on"). Their locations (record_header, list_item, record_related) rendered buttons everywhere, as designed.

The defect (verified against runtime 16.1.0, 2026-07-28)

Two halves of the runtime disagree on the registry key for objectless actions:

  • Registration (AppPlugin boot, collectBundleActions → ql.registerAction): an action without an object registers under the literal key 'global':
    const objectKey = typeof action.object === "string" && action.object.length > 0 ? action.object : "global";
    ql.registerAction(objectKey, action.name, handler, `app:${appId}`);
  • Dispatch (POST /api/v1/actions/<object>/<name> handler): probes the URL's object key, then falls back to '*' — never 'global':
    result = await tryExecute(objectName);
    // on "not found" …
    if (/not found/i.test(msg) && objectName !== "*") result = await tryExecute("*");

Net effect: every objectless body action is registered but permanently unreachable. Reproduced live: clicking Log a Call on a case and submitting returned Action 'log_call' on object '*' not found — the button renders everywhere and works nowhere.

Workaround shipped in the #491 branch

log_call / log_meeting were scoped to objectName: 'crm_case' (the case detail header is where the app explicitly wires them). Scoped, they register under crm_case, the dispatcher finds them, and they execute (verified: POST /api/v1/actions/crm_case/log_call → {"success":true,…} with a sys_activity row created).

Cost of the workaround: the actions are no longer available on leads/contacts/opportunities/accounts — the original "log activity from any record" design is suspended.

Ask

Decide whether to file this against the upstream runtime repo. The fix upstream is one line on either side (register under '*', or have the dispatcher also probe 'global'). Once fixed, restoring the global design here is just deleting the objectName lines from src/actions/global.actions.ts (the file comments point back to this issue).

Activity

  1. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    CollaboratorAuthor

    rc.2 retest: 已解除 — same evidence base as #522

    Retested on @objectstack/* 17.0.0-rc.2 (current main). This issue's specific claim — global body actions registered under 'global' but probed under '*', hence unreachable — no longer has a live instance to reproduce against, and the registry behavior is verifiably fixed:

    • HotCRM's activity actions are now generated per object (Activity model: first-class meetings/events + call logging on every sales object #592), and every per-object dispatch executes with real writes (15/15 combinations verified: crm_event + sys_activity + attendee rows).
    • Probing the global key directly: POST /api/v1/actions/global/log_call → 404 RESOURCE_NOT_FOUND with a governance message citing ADR-0110 D3 — not the rc.0 ghost (200-wrapped "Action 'log_call' on object '*' not found").
    • Handler failures on the /actions path return non-200 (400/404) and surface as red toasts in the console.

    Full evidence and the one residual (automation-resume path still 200-wraps inner failure, but the console reads it) in the #522 comment: #522 (comment)

    Recommend closing alongside #522 after owner confirmation.


    Generated by Claude Code

  2. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    CollaboratorAuthor

    [17.0-rc2验收复核] 结论:已不复现(独立环境复核,与今晨复测结论一致)

    证据:独立 dev server(端口 4091,全新种子库)实测:

    # 1. global 注册键直探
    POST /api/v1/actions/global/log_call
    → 404 {"code":"RESOURCE_NOT_FOUND","message":"Action 'log_call' on 'global' has no declaration — … (ADR-0110 D3) …"}
       (rc.0 的 200 包裹 "not found on object '*'" 幽灵已不存在,失败为显式 404)
    
    # 2. per-object 派发(#592 之后 HotCRM 已无 objectless action)
    POST /api/v1/actions/crm_contact/log_call {"recordId":"-NdOYw…","params":{"subject":"A1 review call","duration":5}}
    → 200 {"eventId":"MolOq0…","activityId":"NwrUm6…","attendeeIds":[…×2]}
       crm_event / sys_activity / crm_event_attendee 三类行逐一 GET 核验落库。
    

    本单描述的「注册在 'global' 下、dispatcher 只探 '*'」的键不一致在 rc.2 上已无活体可复现:未声明键返回治理性 404 而非静默错配,HotCRM 侧的活动 action 全部按对象注册且真实执行。

    环境:hotcrm@0899b4f + @objectstack 17.0.0-rc.2

    建议:前提已过时建议关闭(与 #522 同批,owner 确认后关)。


    Generated by Claude Code

  3. hotlong commented on Aug 14, 2026

    @hotlong
    Contributor

    Closing — provenance

    Closed as part of the GA close-out bookkeeping sweep (#1151, group 3), on the recorded on-card evidence below. No new probe was run; this is a bookkeeping close of a reading already taken.

    (a) Version. @objectstack/* 17.0.0-rc.2, repo at hotcrm@0899b4f. Two independent readings, one from the acceptance re-test and one from an independent dev server.

    (b) Probe. Direct dispatch against the global registry key, plus per-object dispatch of the actions this app actually ships:

    POST /api/v1/actions/global/log_call
    POST /api/v1/actions/crm_contact/log_call  {"recordId":"-NdOYw…","params":{"subject":"A1 review call","duration":5}}
    

    (c) Result. The registry-key disagreement this card reports is gone:

    • The global-key probe returns 404 RESOURCE_NOT_FOUND — "Action 'log_call' on 'global' has no declaration", citing ADR-0110 D3. The dispatcher now resolves under global, the same key registration writes. rc.0's ghost — a 200-wrapped "Action 'log_call' on object '' not found"* — is gone, in both the key it names and the status it returns.
    • Per-object dispatch executes with real writes: crm_event / sys_activity / crm_event_attendee rows each verified by GET after the call; 15/15 combinations green in the companion run on [17.0-rc][疑似平台] 全局(无 objectName)action 无法派发,且 UI 把失败当成功静默吞掉 #522.
    • Handler failures on the /actions path now return non-200 (400/404) and surface as red toasts in the console.

    (d) Source comments.

    Both readings recommend closing.

    (e) Authority. #1150, on maintainer ruling 2026-08-14 (verbatim 「同意」); dispatched as #1151 group 3.

    Two limits of this evidence, stated rather than smoothed over

    1. No live objectless action was dispatched positively. After Activity model: first-class meetings/events + call logging on every sales object #592 generated this app's activity actions per object, HotCRM authors no objectless action at all — the first reading says so directly ("no longer has a live instance to reproduce against"). What is measured is that the dispatcher now looks under global and fails loudly there; what is not measured is a successful end-to-end dispatch of an action registered under global. The 404's governance framing (ADR-0110 D3) also leaves open whether objectless actions remain a supported concept upstream or have been deliberately retired. Either way the defect as this card states it — registered under one key, probed under another, failing silently — no longer holds, and this app no longer depends on the capability.
    2. This card's stored title and body are truncated. Both end mid-sentence at the point where the original text contained an angle-bracketed placeholder (the title at "the dispatcher only probes '", the body at "Dispatch (POST /api/v1/actions/"). That is the GitHub body sanitizer eating a <-prefixed token at rest, not missing authorship. The defect is fully recoverable from the surviving text plus the two re-test comments, so it did not block this reading — but the body should be read as incomplete by anyone revisiting it.

    Generated by Claude Code


    Generated by Claude Code

  4. added
    priority:p1High: required for production / M2
    and removed on Sep 9, 2026
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

    bugSomething isn't workingpriority:p1High: required for production / M2upstream:objectstackBlocked on / caused by the ObjectStack platform — tracked upstream

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions