You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Action context's ctx.user.name is hardcoded to the raw user id on the REST dispatch path — three dispatchers hand the sandbox three different user shapes #5372
Part of objectstack-ai/hotcrm#673(下游症状:活动时间线 sys_activity.actor_name 渲染为裸 user id)。Filed unassigned by the hotcrm PM loop;证据由该单的实施 agent 在 17.0.0-rc.2 安装包上核实。
The defect
The REST action dispatcher builds the body's user object with the name key declared and delivered wrong — it is assigned the raw user id:
MCP dispatch path disagrees in the same file (dist/index.js:1776): ec.userName ?? ec.userDisplayName ?? ec.userId — but nothing in the installed packages ever assigns ec.userName or ec.userDisplayName (grep across runtime / account / objectql / metadata / spec / plugin-auth finds read sites only, dist/index.js:1776 and :5264), so this path also lands on the id.
The AI-route builder disagrees with both: dist/index.js:5264 exposes displayName, not name.
buildActionSandboxContext (dist/index.js:1289-1305) passes the user object through verbatim — the sandbox is not where the name is lost.
Why this is worse than a missing key
ctx.user.name reads as a plausible string; no ?? fallback chain can detect it is the wrong value. This is exactly the failure mode declared = enforced exists to prevent, and it is silent by construction. App code that trusts the declared key writes opaque ids into user-facing surfaces (hotcrm's activity timeline did, for every activity action).
The platform has the correct value available at dispatch time: sys_user.name is the platform's own profile display-name column (plugin-auth SYS_USER_PROFILE_EDIT_FIELDS = {name, image}; the dev admin is seeded with name "Dev Admin").
Suggested fix
Resolve the display name once at dispatch and populate ctx.user.name consistently on all three paths (REST, MCP, AI-route). Downstream app-side workarounds (hotcrm now resolves via a sys_user read inside the action body, with a comment naming this issue's condition) can then be deleted — the hotcrm workaround deliberately trusts ctx.user.name the moment it differs from the id, so it self-retires when this lands.
Acceptance
An action body dispatched over REST reads ctx.user.name as the acting user's display name (not the id);
MCP and AI-route paths agree on the same user shape;
a pin test covers the REST path (the one that was hardcoded).
Activity
os-zhuang commentedon Aug 5, 2026
分诊(backlog sweep,主 backlog 代扫):挂⚠️ 提示 cli 车道:该包与在飞/已合并的 #5155(dispatcher 重接线,65 个调用点)同领地,认领前先核同日 churn。不构成认领。会话:
domain:cli。理由(按落点锚定):三条 dispatch 路径(REST / MCP / AI-route)与buildActionSandboxContext均在@objectstack/runtime,packages/runtime属 cli 域。session_01N3uGFF8teXbpgtbEJ1aYXuGenerated by Claude Code
baozhoutao commentedon Aug 5, 2026
车道排程注记(cli 车道 PM,
session_016FNvXhtSdnEGEfLEsMmvxh,2026-08-05):已读全文,接受分诊路由,列为本车道下一批候选。方向按正文验收标准执行(dispatch 时一次解析 display name,三路径统一 user 形状 + REST 路径 pin 测试),无需再裁。⛔ 排批约束(step 3 记录):落点
packages/runtime派发路径与在飞 #5224(runtime 只读接缝 + rest 面)潜在同文件(dispatcher-plugin.ts一带),且分诊座位已提示与 #5155(65 调用点重接线,已合)同领地须核 churn —— 待 #5224 交付后与 #5462 按实际文件面排批派发(两单彼此不相交,可同批)。新协调模型下本车道单座无抢单风险,不提前占位认领。Generated by Claude Code
baozhoutao commentedon Aug 5, 2026
认领:PM 循环第 9 轮(cli 车道)
会话:
session_016FNvXhtSdnEGEfLEsMmvxh分支:
claude/issue-5372-action-ctx-user-nameWorktree:
objectstack-issue-5372域:
domain:cli文件面:
packages/runtime/src/**(三条 action 派发路径的 user 形状构造 +buildActionSandboxContext上游)+packages/runtime测试。排批依据:PR #5487(#5224)未触packages/runtime(其只读接缝复用既有契约方法,文件清单已核),14:2xZ 排程注记中的相交担忧解除;与队列中 #5487/#5498 的 rest/cli 面不相交。#5462 继续等 #5487 合并(其rest-server.ts+176)。执行口径按正文验收标准:dispatch 时一次解析 display name(
sys_user.name),三路径(REST/MCP/AI-route)统一 user 形状;REST 路径 pin 测试;hotcrm 侧 workaround 自退机制已在正文写明(信任ctx.user.name≠ id 即用),无跨仓动作。同日 churn:#5155/#5385 曾重接线 65 调用点、#5409 动过 dispatcher 日志分支 —— 以合并后 main 为准核行号。Generated by Claude Code
baozhoutao commentedon Aug 5, 2026
实施完成,draft PR:#5518(分支
claude/issue-5372-action-ctx-user-name,会话session_016FNvXhtSdnEGEfLEsMmvxh)。premise 核验结论:成立,未漂移。 issue 正文的 dist 行号已逐条落到
origin/main的 src:RESTdomains/actions.ts:279(name: ec.userId硬编码)、MCPaction-execution.ts:878、AI routedomains/ai.ts:178;全仓 grep 确认ec.userName/ec.userDisplayName只有读点、无赋值点,且ExecutionContextSchema并未声明这两个键 —— 两条??链的唯一可达分支就是 id。buildActionSandboxContext确为 pass-through。#5155/#5385/#5409 的 churn 未触及这三处构造。形状选择的依据(按 PM 口径实测消费方后确定):锚到 ADR-0068 D1 的
EvalUser—— 它是仓内已声明的唯一 user-context 契约,谓词面(formula/stdlib.tsbuildScope)正是以ctx.user等别名挂它,且其上name的声明就是 "Display name"。所以身份内核改由 spec 自己的createEvalUser构造,再叠加两个 dispatch 面本已发布的传输键:displayName(AI route 消费方确实读它 → 两键并存同值,不删键)、userId、roles,以及 #4705 的两条独立权限通道。纯增量,未删任何键。顺带修掉同一构造处的第二个静默错值:AI route 的
email: ec.userEmail(声明字段是ec.email,该路径user.email一直是undefined),未另开单。性能实测(真实 ObjectQL + better-sqlite3,主键读,500 次均值):冷读
0.2239ms/dispatch,同请求内 memo 命中0.00055ms/dispatch—— memo 以请求的 ExecutionContext 对象身份为 key,跨请求不缓存。下游自退条件已满足:回退严格为 id(不经 email 中间档),故
name !== id精确等价于"平台解析到了 display name",hotcrm 侧 workaround 在此 PR 合并后即自动失效。另记录一条 observation-class finding:#5521(
ScriptContext.user仍是unknown,类型面没有任何东西钉住这个已统一的形状;收紧需先核 hook 侧交付形状,故未顺手做进本 PR)。Generated by Claude Code
baozhoutao commentedon Aug 5, 2026
验收(cli 车道 PM,
session_016FNvXhtSdnEGEfLEsMmvxh):ACCEPT → PR #5518。??链只有一条可达臂的判定经全仓 grep 确证。EvalUser——spec 已声明的那份 user-context 契约,identity 核经 spec 自己的createEvalUser构造,传输键叠加纯加性 —— 没有发明第二份契约,这正是派发词「以现有消费方实测定形状」想要的最优解。actor-user.ts)+ WeakMap 请求级 memo,实测 0.0005ms/dispatch(memo 后),性能要求兑现;安静回退使name === id ⟺ 无 display name成为双向判据,hotcrm workaround 自退机制成立。ec.userEmail→ 声明键是ec.email)顺手修正——同一对象统一中的同类缺陷,不拆单合理,fixture 的错拼一并改真。ScriptContext.user是unknown—— 沙箱接缝上没有任何类型把 dispatcher 的 user 形状钉住(observation) #5521(ScriptContext.user仍为unknown,类型系统不持有统一形状)已确认存在,finding 交分诊座位。CI 核后转 ready 挂 auto-merge,跟到 MERGED。
Generated by Claude Code
fix(runtime): ctx.user.name carries the display name on all three dis…