发现自 #4556 的实现过程(PR #4742)跑真实 CLI 时。未认领。与 #4556 无关,任何 bootSchemaStack 系的子命令都能复现。
复现
cd examples/app-crm
OS_DATABASE_URL="file:/tmp/t.db" node packages/cli/bin/run.js migrate recorded-by --json
命令正常返回 JSON、退出码正确。但在返回之后、进程关停时,稳定打出:
ERROR Find operation failed {"object":"sys_metadata","error":{"message":"Unable to acquire a connection", …}}
WARN [integrity] dangling-reference audit could not list an object {"object":"sys_metadata","error":"Unable to acquire a connection"}
ERROR Find operation failed {"object":"sys_view_definition","error":{"message":"Unable to acquire a connection", …}}
WARN [integrity] dangling-reference audit could not list an object {"object":"sys_view_definition","error":"Unable to acquire a connection"}
WARN [integrity] stored references that resolve to nothing (#4551)
{"scanned":0,"dangling":0,"undetermined":0,
"unreadableObjects":["sys_metadata","sys_view_definition"],
"truncatedObjects":[],"references":[]}
即 #4551 的巡检在 stack shutdown 之后才跑到这两个对象,此时 knex 连接池已经关了。
为什么值得修,而不只是噪音
巡检自己的文档把这件事写成了它最在意的一条设计原则:
Unknown and absent are DIFFERENT answers
The single most important property here. An existence probe that cannot run … must never be recorded as "the target does not exist" … Together they are what stops "0 dangling" from ever being read as "everything is fine".
unreadableObjects 这个桶存在的意义,正是让「我没能检查」区别于「我检查了,没问题」。而现在它在每一次正常的 CLI 调用里都非空 —— 一个恒定为真的告警不再携带任何信息,它训练操作者忽略这个桶。等到真有一个对象因为数据源故障读不出来时,那条信息会淹没在同样的两行里。
这也正是 AGENTS.md「Route & surface ownership」第 3 条要防的形态:
3. Absence must be loud. … a verifier that silently degrades (reusing a stale build, skipping a check it could not run) is worse than no verifier, because it reports success.
这里是它的镜像:一个永远报告自己降级了的巡检,与不报告一样没有信息量。
另外那两行 ERROR Find operation failed 是引擎层打的,级别是 ERROR,出现在一条成功的命令输出里 —— 对 CI 日志抓 ERROR 的流水线是直接的误报源。
可能的方向(未验证,需要看巡检的触发时机)
- A. 巡检在 shutdown 之前跑完,或在 shutdown 时跳过。 一次性 CLI 进程本来就不该在关停路径上做完整巡检 —— 它的定位是启动期/巡检期的可观测性,不是退出钩子。
- B. 关停中的连接失败不进
unreadableObjects。 把「引擎正在关停」与「数据源不可达」区分开:前者不是一个发现,不该占用那个桶。
- C. 一次性命令进程默认不跑这个巡检(由
os serve / 巡检专用命令承担)。
倾向 A + B:A 解决时序,B 保证即使时序再次被打破,这个桶也不会被非发现污染。C 单独用的话会让巡检在 CLI 场景永久失明,与它的存在意义相悖。
边界
Refs #4551、#4556(PR #4742)、#4743。
发现自 #4556 的实现过程(PR #4742)跑真实 CLI 时。未认领。与 #4556 无关,任何
bootSchemaStack系的子命令都能复现。复现
命令正常返回 JSON、退出码正确。但在返回之后、进程关停时,稳定打出:
即 #4551 的巡检在 stack shutdown 之后才跑到这两个对象,此时 knex 连接池已经关了。
为什么值得修,而不只是噪音
巡检自己的文档把这件事写成了它最在意的一条设计原则:
unreadableObjects这个桶存在的意义,正是让「我没能检查」区别于「我检查了,没问题」。而现在它在每一次正常的 CLI 调用里都非空 —— 一个恒定为真的告警不再携带任何信息,它训练操作者忽略这个桶。等到真有一个对象因为数据源故障读不出来时,那条信息会淹没在同样的两行里。这也正是 AGENTS.md「Route & surface ownership」第 3 条要防的形态:
这里是它的镜像:一个永远报告自己降级了的巡检,与不报告一样没有信息量。
另外那两行
ERROR Find operation failed是引擎层打的,级别是 ERROR,出现在一条成功的命令输出里 —— 对 CI 日志抓 ERROR 的流水线是直接的误报源。可能的方向(未验证,需要看巡检的触发时机)
unreadableObjects。 把「引擎正在关停」与「数据源不可达」区分开:前者不是一个发现,不该占用那个桶。os serve/ 巡检专用命令承担)。倾向 A + B:A 解决时序,B 保证即使时序再次被打破,这个桶也不会被非发现污染。C 单独用的话会让巡检在 CLI 场景永久失明,与它的存在意义相悖。
边界
sys_metadata_history.recorded_by是lookup('sys_user')却存哨兵字符串'system'——声明的类型与实际存的值不是一回事 #4556 的recorded_by改动 —— 在那条 PR 之前就存在,之后也照旧。readonly收窄与 #4551 的巡检跳过还需要吗?——注释已过期,跳过范围值得收窄 #4743(豁免范围是否收窄)是不同的问题:那条问「该不该查这些字段」,这条问「查的时候连接还在不在」。Refs #4551、#4556(PR #4742)、#4743。