发现于 #5112(#5040 E8 收官验收)的真实 boot 探针。**与 E8 无关的存量缺陷**,按 Prime Directive #10 单独立项。 ## 事实(showcase 真实 boot,`objectstack dev --fresh -p <port> --seed-admin`,47 plugins,含 Audit) 一次 `PUT /api/v1/meta/api/<name>`(Studio 元数据写)之后,日志里成对出现: ``` ERROR Insert operation failed {"object":"sys_audit_log","error":{"message": "insert into `sys_audit_log` (...) values (...) returning * - no such table: sys_audit_log", ...}} WARN Audit write failed {"object":"sys_metadata","action":"create", "err":"... - no such table: sys_audit_log"} ERROR Insert operation failed {"object":"sys_audit_log", ...} WARN Audit write failed {"object":"sys_metadata_history","action":"create", ...} ``` 写本身返回 200,业务数据落库正常;**只有审计行没写进去**,因为表在这个全新数据库里根本没被创建。 ## 为什么这是 error 级而不是噪音 按 AGENTS.md 的「Degradation log levels」判据:降级之后系统从外面看**完全正常**(写入 200,数据在),而它声称会持久化的东西没有落地 —— 这是 durability / data-consistency 一类,不是 functional 一类。审计轨迹是合规面,丢失通常在很久以后、由无法把症状连回这一行的人发现。`WARN Audit write failed` 这一条按该规则应当是 `error`;更重要的是,表缺失本身是要修的。 ## 待查的两个方向(未下结论) 1. AuditPlugin 是否把 `sys_audit_log` 通过 manifest 注册进了 schema sync —— 若注册了,为什么 fresh DB 上没有建表; 2. 还是它依赖某个在 dev 组合里没被装配的 service/对象包。 ## 复现 ``` pnpm dev:showcase -- --fresh -p 39720 --seed-admin --log-level debug # 登录后做任意一次元数据写,例如 PUT /api/v1/meta/api/anything grep "sys_audit_log" <dev log> ``` 关联:#5112(发现于此)、AGENTS.md「Degradation log levels — warn vs error」、#4420(同一类:写入落空只留下没人读的日志)。
Activity
os-zhuang commentedon Aug 4, 2026
认领:PM 循环第 2 轮(v17 C 组工作集,维护者指派)
会话:
session_01FTszibd6C8sUCCZnM4VcrL分支:
claude/issue-5226-fresh-audit-tableWorktree:
objectstack-issue-5226域:标签为
domain:cli,但落点未定 —— 本条按借道认领(见下)文件面(声明,越界即停):
packages/plugins/plugin-audit/**(尤其src/objects/sys-audit-log.object.ts与audit-plugin.ts的 manifest 注册面)、dev stack 装配面(packages/cli/**/ showcase 示例的 stack 配置)、schema-sync 触发路径。⛔ 不碰packages/spec/**,⛔ 不碰.github/workflows/ci.yml(#4928 在飞)、⛔ 不碰packages/spec/src/shared/error-map.*(#4971 在飞)。借道声明(domain lanes 协议)
分诊读码结果:
sys_audit_log的对象声明在packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts—— 按域表packages/plugins/plugin-audit属domain:identity,不在本会话注册的域集 {spec-tooling,cli,devx} 内。但本单真实落点在调查清楚前无法确定(issue 自陈两个待查方向:plugin manifest 是否把对象注册进 schema sync / dev 组合是否根本没装配该 plugin),第二个方向落在 cli 面。按借道三条件逐条核对:
session_015W6nhsDrz6zWQc8je12a1t)10:30Z 交班评论明示「identity 队列:可派项清零」,10:46Z 恢复后范围未变;本单零评论、无 assignee、无分支(git ls-remote | grep 5226空)。pm:dispatched全量(service-analytics 的第二个 SQL 编译器filter-normalizer.buildNode仍带着 #5297 的三条分叉:$not非 NULL-safe、{$not:{}}不加 WHERE、$or的{}析取项被丢 #5325 service-analytics、[spec]api补进 DEFAULT_METADATA_TYPE_REGISTRY 与 BUILTIN_METADATA_TYPE_SCHEMAS(#5206 第 1 步,拆单) #5271/[spec] FILTER_LOGIC_CASES 补空组合子的布尔单位元四条 —— 需与 driver-mongodb 的单位元归约同时落地 #5239/ADR-0120 17.x 收尾块:D5e isolated 安装期姿态硬门 + D6 文档/pin 测试扫荡 + 三姿态 conformance(阻塞于 #5030、#4986 的实现 PR) #5081/ui/ 五个交互配置文件(22 个 z.object 站点)没有任何承载键:实测「无授权门」,按 ADR-0049 定去留 #4988/未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 spec 面、api不在 metadata 类型注册表里 —— Studio 直写路径完全不校验端点,publishPackageDrafts 也没有 E7 门 #5206/[engine] driver-sql 编译 spec 未声明的「数组 where 方言」—— 与 Turso remote 的拒收分叉,需一次定调(接纳进 spec 或响亮弃用) #5158/loadMetaFromDb object branch readsrecord.packageIdfrom a snake_case row — always undefined, every object overlay registers under the 'sys_metadata' sentinel at boot #4636 engine 面、[P3] objectuiActionDef's[key: string]: anyabsorbs every retired and misspelled action key — removing a spec key produces no compile error #4075 objectui,加本会话 formatZodError 把 union 分支的拒绝信息压成 "Invalid input" —— #4001 策展的散文在 CLI 路径上到不了作者 #4971/AREA_REQUIRED_PERMISSIONS_RETIRED 的处方在 #4722 之后过时:仍写着「the server does not walkareas」 #4749/gen:docs 把数组内的 passthrough 对象渲染成Record<string, any>[],抹掉已声明键 —— #4001 战役每个 open 分类站点都会复发 #4912/filterjob 一旦失败,Test Core / Build Core / Dogfood 会全部 skipped 而分支保护判为通过 —— 隐式 success() 今天已第三次咬人 #4928)无一触碰packages/plugins/plugin-audit/**。借道是单单例外,不改登记表归属;identity 车道 PM 若认为该单应归其调度,请在此留言,本会话即刻让行。
本单由维护者在 v17 C 组工作集中点名指派(评估者首跑路径 + 合规面)。
Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
复现结论:前提证伪,根因另在 —— 请重新分诊到 engine 车道
会话
session_01FTszibd6C8sUCCZnM4VcrL,在origin/main(1792384)上按 issue 给的命令跑了真实 boot。症状完全复现,但「表根本没建」这个前提不成立。1. 表建了,而且审计确实在写
boot 日志:
直接查文件:
sys_audit_log?dev.db(主库)dev.telemetry.dblifecycle.class: 'audit'被 ADR-0057 §3.6 路由到专用telemetry数据源(os dev默认建兄弟文件)。这正是 #4887 记录过的「表在另一个 store 里」的误诊形状。所以 issue 里那两个待查方向都可以排除:AuditPlugin 有通过 manifest 注册进 schema sync,dev 组合也没有漏装配任何 service/对象包。
2. 真实根因:ambient 事务跨数据源泄漏
同一次 boot,一次
PUT /api/v1/meta/api/...之后的完整计数:sys_audit_loginsert 尝试 52 次,成功 50,失败 2trxClient.query/execution/transaction.js帧;成功的 50 次一次都没有机制:
protocol.saveMetaItem在主数据源开事务 → 事务内写sys_metadata触发审计钩子 →getDriver('sys_audit_log')正确解析到 telemetry 驱动,但buildDriverOptions(packages/objectql/src/engine.ts:1609)把txStore里属于主库连接的事务句柄无条件塞进 driver options → knex.transacting(trx)把语句发去主库执行 → 主库没这张表。ADR-0067 D2「加入已开启的 ambient 事务」被应用时没有校验该事务是否属于同一个 driver。
3. 影响面比 issue 描述的宽
不限于元数据写。
POST /api/v1/batch+transaction: true写一条普通业务记录也丢:凡是在事务中执行的被审计写入,合规审计行都会丢失 —— 只要部署启用了 lifecycle 数据源分流(
os dev默认开,生产设了OS_TELEMETRY_DB同理)。受影响的是每一个 lifecycle class 为audit/telemetry/event的对象,不止sys_audit_log。4. 已交付 / 未交付
PR #5350(draft)只修了本 issue 的第二个论点 —— 日志级别:
WARN Audit write failed升为error(带后果+修复方向,全进程只报一次),写入点提取为具名persistAuditTrailRow并登记进check:durability-log-level焊死级别。这一条完全在本单声明的文件面内。主缺陷未修。 修复点在
packages/objectql/src/engine.ts的事务管道,属 engine 车道(本单是 identity 借道),且涉及一个本 issue 未给出结论的架构取舍:审计账本大概率倾向 (B)(宁可多记),但这会改变 ADR-0067 的事务语义、影响所有跨数据源写入,不是审计插件能单方面决定的。故此处停手,上报
needs_decision。建议:本 issue 改标题与归属(engine 车道),PR #5350 合并前把
Fixes改成Refs,避免主缺陷随之关闭。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
PM 复核:ACCEPT(仅日志级别半边) → PR #5350;主缺陷前提被证伪,已转 #5351
会话
session_01FTszibd6C8sUCCZnM4VcrL。一、本单主前提被证伪 —— 这是一次成功的派发,不是失败
dev 做了真实复现而非只读代码,结论是本单标题所述不成立:
sys_audit_log表是建了的,在dev.telemetry.db里,50 行。lifecycle.class: 'audit'被 ADR-0057 §3.6 路由到专用 telemetry 数据源,os dev默认以兄弟 SQLite 文件提供一个。本单列出的两个待查方向双双被排除:AuditPlugin 确实通过 manifest 注册进 schema sync;dev 组合也没缺任何包。这是 #4887 记录过的「表在另一个 store 里」误诊形状再现。真因是别的东西,而且更严重:
buildDriverOptions(packages/objectql/src/engine.ts:1609)把 ambient 事务句柄无条件注入,不校验它属于哪个数据源 —— 主库上开的事务,句柄被交给 telemetry 驱动,语句于是发到主库执行。PM 已在origin/main独立核实该代码,其注释自陈「ambient is the safety net」,漏的正是这张网。影响面远宽于本单:
POST /api/v1/batch+transaction: true写普通业务记录同样丢审计行 —— 凡在事务中执行的被审计写入,合规审计行全部静默丢失,涉及每一个 lifecycle class 为audit/telemetry/event的对象。已立 #5351(domain:engine,needs-user-decision),含完整证据、四选项分析与推荐。dev 在真正的修复点前停手上报而非硬修,是对的:落点在 engine 车道、超出本单借道声明的文件面,且涉及一个本单未给结论的语义取舍(外层事务回滚时孤儿审计行怎么办)。
二、日志级别半边:ACCEPT
这一半完全在车道内、判据明确,PR #5350 落地得干净:
error。error同时给出后果与修复方向,且测试断言了这一点(/compliance trail is now INCOMPLETE/、/SUCCEEDED/、/nothing retries it/、/telemetry/、/OS_TELEMETRY_DB=0/)—— 不是「报个错就算」。debug。测试断言 5 次失败 → 1 条error+ 4 条debug+ 0 条warn。理由写得对:审计写发生在每一次数据变更上,逐次报错正是当初让 [automation/approvals] 进程重启后审批决策静默失效:挂起 flow run 仍只存内存(#1518 标记 COMPLETED 但 17.0.0-rc.1 未生效),approve 落库却永不推进且零报错 #4420 的warn无人阅读的反射。persistAuditTrailRow并登记进DURABILITY_CRITICAL_CALLEES,check:durability-log-level守着 —— 声明即强制,防止日后被悄悄改回warn。反向验证也做了:改回warn→ 门禁如期变红并精确点名audit-writers.ts:729。persistAuditTrailRow抽取前后的调用点,activitiesEnabled守卫等价保留,try 块内位置移动不影响语义。never lets a logging failure break the audited write)改动前后都绿 —— 它钉的是本来就成立的性质,保留是防未来重构,没有凑成「全红转全绿」。一处精度提示,不作返工项:changeset 写「全进程只报一次」,而
auditFailureReported是installAuditWriters闭包内的标志,实际是每个 audit-writer 安装实例一次。在多租户 host 上每个引擎各报一次 —— 这个行为本身比真正的全进程去重更合理,只是措辞不够精确。属 patch 级 changeset 的措辞小节,不值得为它多跑一轮。三、本单去向
error并只报一次 (#5226 之二);#5226 主缺陷前提被证伪 #5350 的Fixes #5226已由 PM 改为Refs(dev 主动提出,判断正确)—— 否则合入会自动关掉一个主缺陷未修的 issue。error并只报一次 (#5226 之二);#5226 主缺陷前提被证伪 #5350 合入后关闭,届时其全部可执行内容均已落地:日志级别由 fix(plugin-audit): 审计行写失败升为error并只报一次 (#5226 之二);#5226 主缺陷前提被证伪 #5350 修掉,主缺陷经证伪后转 ambient 事务句柄跨数据源泄漏:凡在事务中执行的被审计写入,合规审计行全部静默丢失(#5226 的真实根因) #5351。在此之前保持 open,免得工作看起来凭空消失。packages/plugins/plugin-audit)随 fix(plugin-audit): 审计行写失败升为error并只报一次 (#5226 之二);#5226 主缺陷前提被证伪 #5350 合入即结束;ambient 事务句柄跨数据源泄漏:凡在事务中执行的被审计写入,合规审计行全部静默丢失(#5226 的真实根因) #5351 属 engine 域,本会话不派,按跨车道协议留给 engine 车道 PM,且它挂着needs-user-decision,维护者裁决前任何人都不该派。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
收尾并关闭 —— 可执行内容已全部落地或转出
会话
session_01FTszibd6C8sUCCZnM4VcrL。PR #5350 已经合并队列合入 main(e18e3da77),落地判据复核通过:本单为什么关闭,而不是留着
本单的两半各有归宿,没有一半是被丢掉的:
error而非WARNe18e3da77)sys_audit_log表没建」dev.telemetry.db里,50 行(ADR-0057 §3.6 把lifecycle.class: 'audit'路由到专用 telemetry 数据源)buildDriverOptions不校验事务属主标题所述的缺陷不存在,所以本单不能以「已修复」的名义留着;而它真正抓到的症状已由 #5351 承接,且 #5351 的作用域比本单宽得多 —— 不限于元数据写,凡在事务中执行的被审计写入,合规审计行全部静默丢失,涉及每一个 lifecycle class 为
audit/telemetry/event的对象。needs-user-decision(外层事务回滚时孤儿审计行的语义要维护者拍板,四选项分析与推荐已附),裁决前任何人都不该派。借道注销
本单是本会话向 identity 域(
packages/plugins/plugin-audit)的单单借道,随 PR #5350 合入即结束。登记表 #4604 同步注销。#5351 属domain:engine,按跨车道协议留给 engine 车道,本会话不派。Generated by Claude Code
fix(plugin-audit): a lost audit row is an `error`, said once (objects…
fix(runtime): 无 `setFallbackHandler` 的适配器改以 `warn` 宣告声明式端点不可达 (object…
baozhoutao commentedon Aug 7, 2026
根因指引(后补,给下一个搜到本单的人):本单的可见症状(元数据写后成对的
ERROR Insert operation failed {object: sys_audit_log}+WARN Audit write failed)的真实根因是 #5351 —— ambient 事务句柄跨数据源穿透:审计写在saveMetaItem的事务内被交给了错误连接。已由 PR #6171(2026-08-07)根因修复:审计类系统账本(声明lifecycle.class∈ audit/telemetry/event)经同源校验移出跨源事务、在属主连接上落盘。另注:本单自陈的前提(「dev --fresh 上
sys_audit_log表根本没建」)已被 #5351 实测证伪 —— 表建在dev.telemetry.db、行在盘上,属 #4887 记录过的「在另一个 store 里」误诊形状;其列出的两个待查方向均被排除,日志级别半边已由 PR #5350 单独落地。本单当时以 completed 关闭是对的,理由以本条为准。Generated by Claude Code
fix(plugin-audit): the Audit write FAILED line names the refused tabl…