Skip to content

fix(automation): kill the four-layer silence around record-change flows that never fire#3099

Merged
os-zhuang merged 2 commits into
mainfrom
claude/stoic-lederberg-80c4f6
Jul 17, 2026
Merged

fix(automation): kill the four-layer silence around record-change flows that never fire#3099
os-zhuang merged 2 commits into
mainfrom
claude/stoic-lederberg-80c4f6

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景

2026-07-17 第三方评估(15.1.x 发布包、全新 create-objectstack 项目):autolaunched record-after-update flow 配置齐全(插件已加载、/meta/flow status=active、triggerType 正确),REST PATCH 满足条件后 flow 不执行,且日志零输出——无注册、无绑定、无跳过、无错误。同样逻辑写成 lifecycle hook 正常。

调查结论

在 15.1.1(本地构建 + npm 发布包逐字节一致)上完整复刻评估者场景:正确配置的 flow 在 os start / os dev / 重启后 / 发布包脚手架项目上端到端全部正常触发。评估者遇到的"不触发"来自某个作者层失配(最可能:start 节点 objectName 与对象注册名不一致——实验证实该失配零日志、静默永不触发),而框架在每一层都沉默:

  1. serve 的 boot 静默窗口吞掉整个 stdout(serve.ts bootQuiet),而 ObjectLogger 的 debug/info/warn 全走 stdout——flow 注册/trigger 绑定恰好全部发生在该窗口内,OS_LOG_LEVEL=debug 也救不回来;只有 error/fatal(stderr)幸存。连"绑定成功"都不可见,失败更不可见。
  2. 触发驱动的 execute() 失败只写 run-history 行,AutomationResult 被绑定回调丢弃——logger 层面零信号。
  3. objectName 失配 → hook 订阅一个没人写的对象名,永不触发,零信号。
  4. requiresautomation/triggers → flow 元数据照常注册(/meta/flow 可见),但引擎/触发器不存在,零信号。

附带发现:draft 状态的 flow 仍会触发(engine 只对 obsolete/invalid 禁用)——与直觉相反,已实验证实并纳入提示语义。

修复(按层)

  • 启动摘要 Flows: 一节(stdout 恢复后打印,不受静默窗口影响):flow 数、绑定数、已注册 trigger 类型、draft 数;外加 ⚠ 行:引擎未启用(附 requires 修复指引)、声明了 trigger 但未绑定、绑定了但对象未知(死绑定)。
  • 触发驱动的运行失败升为 ERROR(stderr,始终可见):engine 绑定回调不再丢弃执行结果;condition 求值异常/节点失败带 flow 名落日志。condition 不满足的跳过保持 debug(高频,有意)。
  • RecordChangeTrigger 绑定时探测对象存在性(feature-detect getObject),失配即 warn 点名。
  • kernel:bootstrapped 绑定审计(automation plugin):每个 enabled-but-unbound 的触发 flow 一条 warn(附原因),新增 AutomationEngine.getTriggerBindingAudit()、扩展 getFlowRuntimeStates()(status/triggerType/object)。
  • os validate 新增 flow 接线检查(@objectstack/lint validateFlowTriggerReadiness):record 触发 flow 指向本 stack 未定义对象 → warning(sys_* 豁免);自动触发 flow status 为 draft(显式或缺省)→ warning(明确"draft 仍会触发,声明 active/obsolete")。showcase 16 个自动触发 flow 补显式 status: 'active'
  • 清理 5 月遗留的 boot 调试残留(registerApp/AppPlugin:init/StandaloneStack/AuditPlugin 共 8 处 stderr 直写)——它们正是前人被同一个静默窗口坑过后的手工探针。

验证

  • 新增失败复现测试先红后绿:trigger 未知对象 warn ×3、触发失败 ERROR ×2、bootstrapped 审计 ×1、lint 规则 ×9、CLI 摘要 ×6。
  • 修改包全量 turbo build+test:60 tasks 全绿(service-automation 306、trigger-record-change 25 等)。
  • 真实项目端到端(worktree 包 + npm 发布包两套):正确 flow 在 PATCH 后照常 stamp;三种作者失配各自出现显式信号(banner ⚠ / stderr ERROR / validate ⚠)。
  • examples/app-showcase os validate 干净通过。

🤖 Generated with Claude Code

…ws that never fire

2026-07-17 third-party eval: an auto-launched flow that failed to fire
produced ZERO output at every layer. Root causes were two-fold:

1. serve's boot-quiet window swallows stdout entirely, and ObjectLogger
   routes debug/info/warn to stdout — so every flow-registration/binding
   log (success AND failure) vanished in `os dev`/`os start`. Only
   error/fatal (stderr) survive.
2. Each "didn't happen" path was itself silent: a trigger-fired execute()
   failure lived only in the run-history row; a flow whose objectName
   matches nothing armed a hook that never fires; a stack whose requires
   lacked automation/triggers just never bound anything.

Fixes, layer by layer:
- Startup banner `Flows:` section (post-restore, immune to the quiet
  window): counts + trigger types + ⚠ lines for engine-not-enabled,
  unbound triggered flows, and dead bindings (unknown object).
- Trigger-fired run failures now log at ERROR (stderr): the engine no
  longer drops the AutomationResult of a trigger-fired execution.
  Condition-not-met skips stay at debug (high-frequency by design).
- RecordChangeTrigger probes object existence at bind time and warns on
  an objectName that matches no registered object (exact-name match).
- kernel:bootstrapped binding audit in the automation plugin: warn per
  enabled-but-unbound triggered flow with the actionable reason; new
  AutomationEngine.getTriggerBindingAudit() + extended
  getFlowRuntimeStates() (status/triggerType/object).
- os validate: new lint rule validateFlowTriggerReadiness — warns when a
  record-triggered flow targets an object the stack does not define, and
  when an auto-triggered flow's status is draft (authored or defaulted;
  draft flows still fire — declare active/obsolete). Showcase flows now
  declare status: 'active' explicitly.
- Removed the leftover boot-debug stderr writes (registerApp/AppPlugin/
  StandaloneStack/AuditPlugin) that earlier debugging of this same
  silence left behind.

Verified end-to-end on a real create-objectstack project (published-
package layout): correct flows fire on REST PATCH across os start /
os dev / restart; each mistake variant now produces its explicit signal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 17, 2026 7:50am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/l labels Jul 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 7 package(s): @objectstack/cli, @objectstack/lint, @objectstack/objectql, @objectstack/plugin-audit, @objectstack/runtime, packages/services, packages/triggers.

41 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/cli)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via packages/cli, @objectstack/runtime)
  • content/docs/automation/webhooks.mdx (via packages/services)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-audit, @objectstack/runtime)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql, @objectstack/runtime)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/plugin-audit)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli, @objectstack/runtime)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli, @objectstack/objectql, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/plugins/index.mdx (via @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/objectql, @objectstack/plugin-audit, @objectstack/runtime, packages/services)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql, @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli, @objectstack/objectql, @objectstack/plugin-audit, @objectstack/runtime)
  • content/docs/releases/v9.mdx (via @objectstack/objectql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

The preview deploy for 59931e6 failed on Vercel's side while all 15
GitHub checks passed. The docs build graph is {@objectstack/docs,
@objectstack/spec} — zero overlap with this branch's packages — and the
commit touches no package.json/lockfile, so the failure cannot originate
here (`turbo run build --filter=@objectstack/docs --force` passes
locally). Vercel posts a commit status rather than a check run, so a new
commit is the only way to retrigger the deployment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit 2ea08ee into main Jul 17, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/stoic-lederberg-80c4f6 branch July 17, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant