Repository navigation
[17.0-rc][疑似平台] console 表单无视 visibleOn,并强制其 required —— crm_lead 在 UI 上完全无法创建 #688
Description
Found during the 17.0 GA acceptance sweep on @objectstack/* 17.0.0-rc.2 (current main, commit 5a78f88). Likely the highest-impact UI defect in this run: it blocks the app's headline demo path.
Symptom
A new lead cannot be created through the console at all.
Reproduction
- Console → HotCRM → Leads → New.
- Fill First Name, Last Name, Company, Email; set Status =
New. - Click Create.
Observed — submit is blocked client-side with five errors for fields that cannot apply to a brand-new lead, and zero network writes fire:
Disqualification Reason is required
Duplicate Of is required
Duplicate Of Lead is required
Duplicate Of Contact is required
Duplicate Status is required
All five render with a red required asterisk. The form even prints its own helper text "Required when status is Unqualified" directly above the error "Disqualification Reason is required" — self-contradictory on screen.
The form is literally unsatisfiable: it demands the new lead be declared a duplicate of both an existing lead and an existing contact at once. duplicate_of_lead and duplicate_of_contact are mutually exclusive by design — each is gated on duplicate_of_type being the other one.
Screenshot: shots/uif-crm_lead-02-after-submit.png (acceptance run W1).
The app metadata is correct — the renderer ignores it
Every one of the five fields is declared in src/views/lead.view.ts with a visibleOn predicate that is false for a new lead:
{ field: 'disqualification_reason', required: true, visibleOn: 'status == "unqualified"' },
{ field: 'duplicate_of_type', required: true, visibleOn: 'disqualification_reason == "duplicate"' },
{ field: 'duplicate_of_lead', required: true, visibleOn: 'duplicate_of_type == "crm_lead"' },
{ field: 'duplicate_of_contact', required: true, visibleOn: 'duplicate_of_type == "crm_contact"' },
{ field: 'duplicate_status', required: true, visibleOn: 'disqualification_reason == "duplicate"' },The renderer displays these fields while their visibleOn is false, and enforces their required: true.
The server does not agree with the form. The object-level counterparts use requiredWhen predicates (lead.object.ts:369,375) and are correctly not enforced: POST /api/v1/data/crm_lead with the same field set returns 201. So the condition is honoured server-side and ignored form-side.
Scope
Any object whose form declares visibleOn together with required: true. crm_lead is where it is fatal because the conditional fields are required; elsewhere it will surface as fields that should be hidden appearing on the form.
Workaround
REST creation is unaffected (201). There is no UI workaround — the form cannot be satisfied.
Refs #515
Activity
docs(acceptance): record the object CRUD write phase and the six defe…
docs(acceptance): finalize the rc.2 report as a partial run with a GA…
docs(acceptance): add the handoff page and commit the run's text evid…
docs(acceptance): record the full action sweep and all ten blocker ve…
docs(acceptance): finalize — permission matrix does not hold; all 8 s…
docs(acceptance): mark the handoff page complete — all 8 sections exe…
yinlianghui commentedon Aug 5, 2026
分诊(PM 循环第 1 轮,会话
session_01VHrPAGEgFDoHjphqYG4BMa):本单拆成两半,hotcrm 半侧可修且应修,入pm:queue;平台半侧已有既存单,不再立镜像。根因已定位:objectstack#5149(2026-08-04)实测了 console 的谓词求值是 fail-open(求值失败 → 默认可见),且值绑定在
record命名空间下 —— 裸字段名谓词(status == "unqualified")引用的是未绑定标识符,永远求值失败,于是字段常显 +required被强制,恰是本单症状。hotcrm 半侧(可立即修):
src/views/lead.view.ts正是两种方言混用的现场 —— 20+ 处裸字段名字符串谓词(L26–L856),而同文件 L838/L843 已有正确写法Precord.rating >= 3,object 侧 `requiredWhen` 也全部是 `P`…record.…(服务端正确执行的那组)。把全部视图文件的裸名谓词改写成 P 标签 +record.前缀方言,console 即可正确求值visibleOn,P0 的「UI 无法建线索」不等平台修复即可解除。这不是 workaround,是把元数据改写成平台实际的谓词契约。下一轮派发,并加守卫测试禁止裸名谓词回流。平台半侧(fail-open 无诊断):Blocked-by: objectstack-ai/objectstack#5149 —— fail-closed / 响亮报错属平台修法,静默 fail-open 是 AI 作者批量犯错被掩盖的温床,本单症状就是实例,已在该单可见。
Generated by Claude Code
yinlianghui commentedon Aug 5, 2026
派发依据(前次分诊已核):objectstack#5149 实测 console 谓词求值把值绑定在
record命名空间、失败即 fail-open —— 裸名谓词永远求值失败、字段常显且required被强制,恰是本单「UI 无法建线索」的机理;lead.view.ts:838/843已有正确的Precord.…`` 写法与 object 侧全部requiredWhen同方言。修完本单,P0 症状不等平台修复即可解除。Generated by Claude Code
yinlianghui commentedon Aug 5, 2026
验收 ACCEPT(PM 复核,对照 GitHub 实况):PR #726(hotcrm 半侧),CI 9/9 绿,已转 ready 并挂自动合并。
复核要点:
@objectstack/formula求值器):(a) 裸名谓词对任何记录都ok:false "Unknown variable"(值无关);(b)record.前缀两个方向均正确;(c) 关键修正 —— 只加前缀不够:新建表单的记录键不存在,严格 CEL 报No such key照样 fail-open,P0 不会解除。因此 21 处谓词全部改成 P 标签 +record.前缀 +has()守卫(序比较补!= null)—— 这不是扩范围,是 AGENTS.md 对visibleWhen既有的 totality 家规(Script validations are silently SKIPPED when the merged record lacks the field key (update path) #630)在视图层的补课。已正确的两处(L838/843)也补了守卫。visibleOn现与lead.object.ts的requiredWhen逐字符相同 —— 显示时机 = 服务端要求时机,结构上排除了「表单不可满足」的回归。buildScope读出(跟随平台不漂移)、三种记录形态(空 / 全填 / 全 null)可求值、磁盘计数防空转、裸名前提正向 pin(平台若开始绑定裸名会响亮变红)。反向验证 6 红 5 绿与预测一致,全 null 形态还抓到dyn<null> >= int中止,证明该 pin 非装饰。check-nul-bytes.mjs,dev 自行更正为实际执行的 grep 自扫 —— 证据必须可复跑,这个更正是对的。状态归位:hotcrm 半侧到此完成,本单保持 open 仅跟踪平台半侧(fail-open 无诊断,Blocked-by objectstack#5149)—— 摘 assignee、
pm:dispatched→pm:blocked;P0 症状(UI 无法建线索)已由本 PR 解除,优先级相应降至 p2。objectstack#5149 落地后,test/view-predicate-dialect.test.ts的裸名前提 pin 会变红,即为复核信号。Generated by Claude Code
yinlianghui commentedon Aug 5, 2026
[17.0-rc2验收] 测试组 A6 补充影响面量化(
hotcrm@0899b4f + @objectstack 17.0.0-rc.2,17 个 crm_* 对象的新建表单逐一实测):波及范围:仅 crm_lead。 对 17 个对象分别对比「表单渲染的必填标记」vs「对象元数据 required」:16 个对象完全一致(crm_case 表单多出的
crm_account必填是case.view.ts:168显式声明的,属有意授权),只有 crm_lead 多出本单所述 5 个条件字段(disqualification_reason / duplicate_of_type / duplicate_of_lead / duplicate_of_contact / duplicate_status)—— 即visibleOn+required组合被无视的对象面=1/17。一处修正:表单并非严格「不可满足」,但代价是数据污染。 5 个被强制的条件字段全部可在 UI 里给值(两个 lookup 都有候选),都填上后 lead 能建成。实测建出的记录(REST 读回):
{"status":"new","disqualification_reason":"not_a_fit","duplicate_of_type":"crm_lead", "duplicate_of_lead":"72AlZBUF8XzrLqGx","duplicate_of_contact":"-79jZx6f6I4luy1y", "duplicate_status":"suspected"}一条全新 lead 被迫携带「不合适 + 同时是某 lead 和某 contact 的疑似重复」的自相矛盾标记 —— UI 能走通的唯一路径就是写入垃圾数据,严重度不因此降低(headline demo 路径仍等于不可用)。
Generated by Claude Code
yinlianghui commentedon Aug 6, 2026
📡 上游动静(修复线 PM 例行扫描,2026-08-06):objectstack#5149 已裁决(平台侧 needs-user-decision 于 08:29Z 落锤):维持 fail-open 不翻转(已发货应用行为不变),但「静默」被裁定不可接受——objectui 侧两项即刻入队(evalFieldPredicate 去静默 catch,每谓词 warn 一次带源文本与失败原因;
record./data.绑定写进文档),另拆一单把裸标识符 lint 扩到 view/page 谓词(坏谓词构建期拦下)。对本单的影响:hotcrm 侧的
record.前缀修复与钉子测试已随 PR #726 落地,行为面无需再动;剩余价值是「坏谓词可见性」——待 objectui 的 warn 落地并随某个 rc 到达后,本单的诊断诉求即被满足,可关单。继续 pm:blocked 跟踪,下次平台升级单探测该 warn 是否已到。Generated by Claude Code
fix(views): bind lead form predicates to `record` and make them total (…
test(flows): retire the F1 readonly-write half objectstack lint alrea…