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/lintvalidateFlowTriggerReadiness):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-showcaseos 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

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 17, 2026 7:50am

Request Review

@github-actionsgithub-actionsBot 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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang