Repository navigation
Every one of the 24 flows fails its cold-boot re-bind with a validation error the log truncates to [ #653
Description
Activity
- addedbugSomething isn't workingSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfacesDeclarative metadata — schema, security posture, UI surfacespm:queueReady for the PM dispatch loopReady for the PM dispatch loop
on Aug 2, 2026 已入队
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
- addedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Aug 4, 2026 认领: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
诊断完成。前提已过期,两个缺陷都在平台侧,其中第 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 --noEmitexit 0;vitest run49 files / 1168 passed。未触碰 #650 的 decision 节点条件。
Generated by Claude Code
验收(PM 循环第 1 轮,会话
session_01Rvtsew6XgsRK全 ID 见认领评论):ACCEPT —— PR #667,已转 ready 并入合并队列。复核要点(对照 GitHub 核实,非转述报告):
- 前提已过期,且被正确证伪:当前 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(日志截断)仍活着,已按分诊要求上报 bug(service-automation): flow 绑定失败的告警把 Zod issue 数组塞进单行日志,读者只拿到一个孤零零的
[objectstack#5048(附完整错误、最小复现、三处同形态绑定点的类别性修法建议),未在本仓绕过 —— 符合 contract-first。 - 本仓交付:
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 通过,负向检查证实失败信息不截断。 - A
decisionnode's singularconfig.conditionis never evaluated — 24 flows author inert metadata #650(decision 节点条件)范围未被触碰,两单未碰撞 —— 与派发时的串行担忧解除。
Generated by Claude Code
- 前提已过期,且被正确证伪:当前 origin/main(17.0.0-rc.2)上 24 条告警全部消失,重绑定实测跑通(
- added a commit that references this issue
on Aug 10, 2026 - addedpriority:p1High: required for production / M2High: required for production / M2and removed
on Sep 9, 2026
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 --freshemits 24 warnings — one per registered flow — from the automation service'skernel:readyre-bind: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, onmainwith no local edits.Why the message is a bare
[syncFlowsFromProtocolin@objectstack/service-automationlogs`…failed to register ${def.name}: ${err.message}`, and the logger writes oneline per record.
err.messagehere begins with[\n {\n "code": …— a Zodissue 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:
Why it is not (visibly) breaking anything
The re-bind is deliberately additive — the doc comment above it says so:
The earlier boot pull registers the flows from the artifact, and those
registrations survive. Record-triggered automation demonstrably works: raising
crm_opportunity.amountto $310K on a fresh install opens the approval requestand 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_metadatarather than the artifact — inherits a flow set ofzero, silently.
Repro
What is needed
err.message(or the Zod issues as structured data) so thewarning names the field that fails. A truncated diagnostic is worse than
none: it costs a boot to read and tells you nothing.
FlowSchemarejects, 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 (
decisionnodeconfig.conditioninert), #643 / #649 (flowcondition totality) — same subsystem, different layer.