fix(plugin-audit): 审计行写失败升为 error 并只报一次 (#5226 之二);#5226 主缺陷前提被证伪 - #5350
Merged
Conversation
An audit-write failure is a durability / data-consistency degradation, not a functional one: the audited write itself succeeds, its row is on disk and the API returns 200, so nothing looks broken from the outside while the `sys_audit_log` entry recording WHO did it never landed and nothing retries it. AGENTS.md "Degradation log levels" puts that at `error`; it was at `warn`. The error names both things such a line owes: the consequence (the compliance trail is now incomplete, and the system will keep looking healthy) and the fix (ADR-0057 lifecycle-class routing sends sys_audit_log to the `telemetry` datasource when one is registered, so "no such table" here means the write ran against a different datasource than the one holding the table; OS_TELEMETRY_DB=0 collapses the split). Reported ONCE per process, not once per failed write — an audit write runs on every mutation, and one error per write is what trained everyone to skim the channel in #4420. Subsequent failures degrade to `debug`. The write is extracted as a named `persistAuditTrailRow` callee and registered in DURABILITY_CRITICAL_CALLEES so `pnpm check:durability-log-level` holds the level; verified by reverting it to `warn` and watching the gate go red. NOTE: this does NOT fix the missing-table symptom #5226 reports. That premise was disproven on a real `dev --fresh` boot — the table IS created (in dev.telemetry.db, 50 rows) — and the real defect is an ambient transaction leaking across datasources in the engine. See the PR body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTszibd6C8sUCCZnM4VcrL
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 5, 2026
os-zhuang
marked this pull request as ready for review
August 5, 2026 00:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #5226
前提证伪:表是建了的
issue 断言「表在这个全新数据库里根本没被创建」。在
origin/main(1792384)上跑真实复现,这一条不成立。boot 日志里 provisioning 明确成功:
直接查两个 SQLite 文件:
sys_audit_logdev.db(主库)dev.telemetry.dbsys_audit_log的lifecycle.class: 'audit'被 ADR-0057 §3.6 路由到专用telemetry数据源,os dev默认以兄弟文件形式提供一个。表建好了,50 行审计也确实落盘了。#4887 早就记录过这个「在另一个 store 里」的误诊形状。真实根因:ambient 事务跨数据源泄漏
同一次 boot 的完整计数(登录后做一次
PUT /api/v1/meta/api/issue5226probe):sys_audit_loginsert 尝试 52 次trxClient.query/execution/transaction.js帧失败的两条正是 issue 贴出的那两条(
sys_metadata、sys_metadata_history)。机制:protocol.saveMetaItem在主数据源上开启事务;sys_metadata触发afterInsert钩子 → 审计写;getDriver('sys_audit_log')正确解析到 telemetry 驱动;buildDriverOptions(packages/objectql/src/engine.ts:1609)把txStore里的 ambient 事务句柄无条件塞进 driver options —— 那个句柄属于主库连接;.transacting(trx)于是把语句发到主库执行,而主库没有这张表 →no such table: sys_audit_log。即 ADR-0067 D2「加入已开启的 ambient 事务」被应用时没有校验该事务是否属于同一个 driver。
影响面比 issue 描述的宽
不限于元数据写。用
POST /api/v1/batch+transaction: true写一条普通业务记录showcase_category,同样丢审计行:结论:凡是在事务中执行的被审计写入,其合规审计行都会丢失 —— 只要该部署启用了 lifecycle 数据源分流(
os dev默认开;生产上设了OS_TELEMETRY_DB亦然)。这不是 plugin-audit 的问题,也不是「plugin 声明的对象没进 schema sync」:所有 plugin 声明对象都正常建表,sys_comment因为没有 lifecycle class 留在主库、从不失败。受影响的是每一个 lifecycle class 为audit/telemetry/event的对象。本 PR 做了什么(只有第二个缺陷)
真实修复点在
packages/objectql/src/engine.ts的事务管道 —— 属 engine 车道,在本单声明的文件面之外,且涉及一个 issue 未给出结论的架构取舍(跨数据源写入在外层事务回滚时应当:一起回滚?自动提交并接受孤儿审计行?还是直接拒绝?)。按借道声明与「不猜测公共契约」的规矩,此处停手并上报needs_decision,不在本 PR 里动它。本 PR 只落地 issue 的第二个论点 —— 日志级别,这一条完全在本单车道内且判据明确:
按 AGENTS.md「Degradation log levels」的那一个问题:降级之后系统从外面看是否仍然正常,而它声称已持久化的东西没落地?被审计的那次写入本身成功、数据在盘上、接口 200,只有记录「谁做的」的审计行没了,且无人重试 —— 是 durability/data-consistency 类,应为
error。WARN Audit write failed→error,并在第一行同时给出后果(合规轨迹已不完整、系统会继续显得健康)与修复方向(表被 ADR-0057 分流到telemetry;"no such table" 通常意味着写入执行在了与建表处不同的数据源上;OS_TELEMETRY_DB=0可收回分流)。warn无人阅读的反射。后续失败降debug,细节仍可取回。persistAuditTrailRow,登记进DURABILITY_CRITICAL_CALLEES,由pnpm check:durability-log-level把级别焊死。验证
反向验证(方向为事前预测的 before-red / after-green):
reportAuditWriteFailure改回warn→ 门禁按预期变红,精确点名audit-writers.ts:729 guards persistAuditTrailRow()。logger.warn('Audit write failed', …)→ 新增 4 个用例中的 3 个变红(Tests 3 failed | 109 passed)。诚实地说明第 4 个:
never lets a logging failure break the audited write改动前后都绿 —— 旧代码同样把日志调用包在try里。它固化的是一条本来就成立的性质,不是本次改动带来的新覆盖,保留是为了防止将来重构时丢掉。遗留
主缺陷(跨数据源 ambient 事务)未修,#5226 需重新分诊并转派 engine 车道。
🤖 Generated with Claude Code
https://claude.ai/code/session_01FTszibd6C8sUCCZnM4VcrL
Generated by Claude Code
Generated by Claude Code