Skip to content

Every one of the 24 flows fails its cold-boot re-bind with a validation error the log truncates to [ #653

Description

@os-zhuang

Found while measuring #640 on a fresh install; filed unassigned per Prime Directive #10 and deliberately not fixed there.

What happens

Every boot of objectstack dev --fresh emits 24 warnings — one per registered flow — from the automation service's kernel:ready re-bind:

WARN [Automation] cold-boot flow bind: failed to register campaign_enrollment: [
WARN [Automation] cold-boot flow bind: failed to register case_escalation: [
WARN [Automation] cold-boot flow bind: failed to register case_escalation_on_create: [
… 21 more, ending with …
WARN [Automation] cold-boot flow bind: failed to register task_due_reminder: [

That is all 24 flows in allFlows, not a subset — nothing is spared, including flows that were untouched for months.

Measured on 17.0.0-rc.1, pnpm dev --fresh, empty database, on main with no local edits.

Why the message is a bare [

syncFlowsFromProtocol in @objectstack/service-automation logs
`…failed to register ${def.name}: ${err.message}`, and the logger writes one
line per record. err.message here begins with [\n {\n "code": … — a Zod
issue array — so everything after the opening bracket is on the next lines and
never reaches the log. The result is 24 warnings a maintainer cannot act on:
they name the flow but not the problem.

So there are two defects stacked, and the second one is why the first has gone
unnoticed:

  1. every flow fails a schema validation on the re-bind path, and
  2. the warning is unreadable, so nobody knows what the validation says.

Why it is not (visibly) breaking anything

The re-bind is deliberately additive — the doc comment above it says so:

Bind flows from the protocol's flattened flow view at kernel:ready, so
record-triggered automations actually bind on a fresh start (#2560).
Additive by design … a transient empty/failed read at boot can't unbind the
flows the boot pull already registered.

The earlier boot pull registers the flows from the artifact, and those
registrations survive. Record-triggered automation demonstrably works: raising
crm_opportunity.amount to $310K on a fresh install opens the approval request
and routes it. So this is currently noise plus an unproven path, not an
outage.

That is exactly what makes it worth a look rather than a shrug: the path that
re-hydrates flows from stored metadata rejects 24 out of 24 flows, and the
only reason the app works is that a different path already registered them. Any
scenario that leans on the stored view alone — a metadata reload, a host that
boots from sys_metadata rather than the artifact — inherits a flow set of
zero, silently.

Repro

pnpm dev --fresh -p 41874 2>&1 | grep "cold-boot flow bind" | wc -l   # → 24

What is needed

  1. Log the whole err.message (or the Zod issues as structured data) so the
    warning names the field that fails. A truncated diagnostic is worse than
    none: it costs a boot to read and tells you nothing.
  2. Then find out what the flattened protocol view carries that FlowSchema
    rejects, and fix it at the producer — whether that is our metadata or the
    platform's flatten step. If it turns out to be platform-side, this belongs
    upstream in objectstack with the message from step 1 attached.

Related: #650 (decision node config.condition inert), #643 / #649 (flow
condition totality) — same subsystem, different layer.

Activity

  1. added
    bugSomething isn't working
    metadataDeclarative metadata — schema, security posture, UI surfaces
    pm:queueReady for the PM dispatch loop
    on Aug 2, 2026
  2. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    已入队 prio:p1。分诊说明:两个缺陷里,先修第 2 个。

    正文把顺序排对了,值得强调一下为什么:日志被截断成一个孤零零的 [,不只是"体验不好"——它是第 1 个缺陷至今没人发现的原因。24 条警告每条都点名了流程却不说问题,读一遍要付一次启动的代价,收获为零。所以"把 err.message 完整打出来(或者把 Zod issues 作为结构化数据输出)"不是顺手改进,它是让第 1 个缺陷第一次变得可被诊断的前提。在拿到那条真正的校验信息之前,任何对根因的猜测都是在猜。

    优先级给 p1 而不是 p0,依据是正文自己给的证据链:artifact 启动那一路已经注册过流程,重绑定是加性的,record-triggered 自动化实测可用($310K 商机确实开出审批并路由)。所以今天不是故障。但"今天不是故障"完全依赖于另一条路径先跑过——任何只依赖存储视图的场景(元数据热重载、从 sys_metadata 启动而非从 artifact 启动的宿主)会静默地拿到一个空流程集。这是"能用是因为走运",不是"能用是因为设计如此"。

    派发时的指示:拿到完整错误信息后,先判定拒绝来自我们的元数据还是平台的 flatten 步骤,再决定在哪修。如果是平台侧,连同第 1 步拿到的信息一起上报 objectstack,不要在本仓库绕过去。

    同族参考:#650(decision 节点 config.condition 惰性)、#643 / #649(流程条件全函数化)——同一个子系统的不同层。


    Generated by Claude Code

  3. self-assigned this
    on Aug 4, 2026
  4. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 1 轮
    会话:session_01Rvtsew6XgsSjxVa59HRPRK
    分支:claude/issue-653-flow-rebind-diagnosis
    Worktree:hotcrm-issue-653
    文件面:诊断先行;若拒绝源于本仓元数据则 src/flows/** + 测试;若是平台 flatten 步骤则上报 objectstack(带上完整错误),不在本仓绕过

    stale-premise 提示:本 issue 在 17.0.0-rc.1 上测量;main 已于 08-03 升级 17.0.0-rc.2 并迁移了它破坏的元数据(#663)。派发词已要求先在当前 origin/main 上复测 24 条警告是否仍在、形态是否变化。


    Generated by Claude Code

  5. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    诊断完成。前提已过期,两个缺陷都在平台侧,其中第 1 个已随 rc.2 修好。

    复测(当前 origin/main,17.0.0-rc.2)

    24 条告警全部消失,而且重绑定路径确实跑了、确实全绑上了:

    $ grep -c "cold-boot flow bind" boot.log          → 0
    INFO [Automation] Bound 24 flow(s) from the protocol at kernel:ready
    INFO [Automation] 24 flow(s) registered, 18 bound to triggers, 0 unbound-but-triggered
    

    那个 [ 说的是什么

    把 rc.2 的修复在 node_modules 里临时改回 rc.1 行为(readFlowDefsFromProtocol:return stripReadDecorations(doc) → return doc),24 条告警逐字复现;再把日志里的换行换成可见字符重跑,才看到全文:

    [ { "code": "unrecognized_keys", "keys": [ "_diagnostics" ], "path": [], "message": "Unrecognized key(s) on this flow: `_diagnostics`. …" } ]
    

    被拒的键不是我们写的:getMetaItems({ type: 'flow' }) 给每个 item 打上 _diagnostics 装饰,冷启动重绑定又把这份被服务过的文档原样喂回 FlowSchema(#4001 关闭 schema 后未知键改为抛出)——读取路径栽在自己的输出上。上游 rc.2 changelog 确认同一件事(5b843fb / cloud#971),修法是在读取缝合处 strip,而不是放宽 FlowSchema。

    归因用对照坐实过:rc.1 引擎 × 当前 flows、rc.1 引擎 × #663 之前真实 build 出的 flows,24/24 全部注册成功——artifact 形态从来没被拒过,被拒的只有装饰后的协议视图。本仓元数据从头到尾无责,src/flows/** 一行未改。

    第 2 个缺陷仍然活着 → objectstack-ai/objectstack#5048

    截断在 rc.2 一行未动:三个绑定点都是 ${err.message} 单行插值,下一个真绑不上的 flow 还是只会给出一个 [。已上报上游 objectstack-ai/objectstack#5048,附完整错误、最小复现、根因与建议修法(Zod issues 作为结构化数据交给 logger 第二参数)。分诊里"是第 2 个缺陷让第 1 个隐了身"这个判断,被上游 changelog 直接印证了。

    本仓交付:#667(draft)

    不改元数据,只钉住 app 这一半——test/flow-cold-boot-rebind.test.ts 把每个 flow 过一遍重绑定真正调用的 AutomationEngine.registerFlow(先 JSON round-trip,因为重绑定看到的是存储态文档),失败时打印完整的 issue 数组;并重建 #653 的失败类别:被装饰的 flow 必须仍被拒(schema 保持关闭),stripReadDecorations 之后必须重新可注册,断言以 METADATA_READ_DECORATIONS 为准。

    tsc --noEmit exit 0;vitest run 49 files / 1168 passed。未触碰 #650 的 decision 节点条件。


    Generated by Claude Code

  6. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    验收(PM 循环第 1 轮,会话 session_01Rvtsew6XgsRK 全 ID 见认领评论):ACCEPT —— PR #667,已转 ready 并入合并队列。

    复核要点(对照 GitHub 核实,非转述报告):

    1. 前提已过期,且被正确证伪:当前 origin/main(17.0.0-rc.2)上 24 条告警全部消失,重绑定实测跑通(Bound 24 flow(s) … at kernel:ready)。缺陷 1 的根因是平台读取路径的 _diagnostics 装饰喂回关闭后的 FlowSchema(#4001),上游已在 rc.2 修复(cloud#971,经 chore(deps): upgrade ObjectStack to 17.0.0-rc.2 and migrate the metadata it broke #663 继承)。本仓元数据从头就没有问题 —— 归因用 rc.1 引擎 × 两个 flows 版本的对照实测坐实,不是从 changelog 推的。
    2. 缺陷 2(日志截断)仍活着,已按分诊要求上报 bug(service-automation): flow 绑定失败的告警把 Zod issue 数组塞进单行日志,读者只拿到一个孤零零的 [ objectstack#5048(附完整错误、最小复现、三处同形态绑定点的类别性修法建议),未在本仓绕过 —— 符合 contract-first。
    3. 本仓交付:test/flow-cold-boot-rebind.test.ts 把全部 flow 过真实 AutomationEngine.registerFlow(JSON round-trip 还原存储态),失败时输出完整 issue 数组;并重建 Every one of the 24 flows fails its cold-boot re-bind with a validation error the log truncates to [ #653 失败类别双向断言(装饰键必须仍被拒 = schema 保持关闭;strip 后必须可注册 = rc.2 解法仍有效,键集取自 METADATA_READ_DECORATIONS 自动覆盖将来新增)。+ changeset。全套 1168 通过,负向检查证实失败信息不截断。
    4. A decision node's singular config.condition is never evaluated — 24 flows author inert metadata #650(decision 节点条件)范围未被触碰,两单未碰撞 —— 与派发时的串行担忧解除。

    Generated by Claude Code

  7. 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

Labels

bugSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfacespm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looppriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions