Repository navigation
plugin-audit never provisions sys_audit_log / sys_activity — Setup "System Overview" audit widgets silently render zeros #4887
Description
What happens
On a freshly seeded database with plugin-audit loaded, every load of Setup →
System Overview logs a burst of:
WARN [Analytics] dataset "sys_audit_log_metrics" backing object "sys_audit_log" is unavailable
(SELECT COUNT(*) AS "event_count" FROM "sys_audit_log" WHERE (action = 'login' AND …)
- no such table: sys_audit_log); returning an empty result instead of failing the widget
12 of these per dashboard load in my session (login / permission_change /
config_change counters, plus the GROUP BY action and GROUP BY user_id charts).
The tables are genuinely absent:
sys_audit_log MISSING
sys_activity MISSING
sys_comment EXISTS
(88 tables in the DB; Audit is in the loaded-plugin list.)
Why this is worse than log noise
The analytics layer degrades gracefully — empty result instead of a failed widget
— so the client sees nothing wrong. My browser sweep recorded
/_console/apps/com.objectstack.setup/dashboard/system_overview as completely
clean: no failed requests, no console errors, no error text.
So the dashboard renders 0 logins, 0 permission changes, 0 config changes and
looks authoritative doing it. That is the "Total Spend: 0 on a populated table"
failure mode rest-server.ts calls out by name in the analytics resolver comment
— except here it is structural rather than a wrong-database mistake, and it is
the security dashboard reading zero.
The guard that should prevent this exists and isn't taking effect
plugin-audit/src/audit-plugin.ts has provisionSystemTables() (~L168-198),
added for precisely this, with a docblock that describes today's symptom:
sys_audit_log / sys_activity / sys_comment are otherwise lazy-created on first
WRITE … A freshly provisioned env that READS one first … hits SQLite "no such
table" … The UI degrades to an empty feed, but the log is noisy and can mask
real errors.
It is wired at kernel:ready (~L80, L97) and loops [SysAuditLog, SysActivity, SysComment] calling engine.syncObjectSchema(obj.name).
Two of its three tables are missing anyway, and no AuditPlugin: could not provision … warning appears in the log — so the per-object catch never fired.
syncObjectSchema does exist on the ObjectQL engine (objectql/src/engine.ts:5628),
so the early if (typeof sync !== 'function') return; bail is the most likely
path, but I did not confirm which of the two silent exits is taken — that belongs
to the fix. sys_comment existing while the other two don't suggests it gets
created by some other path rather than by this loop succeeding.
Worth noting the early return is silent: if provisioning is skipped wholesale,
nothing says so.
Suggested direction
- Find why the
kernel:readyprovisioning doesn't reachsyncObjectSchemafor
these objects, and make both bail-outs audible (alogger.warnon the
typeof sync !== 'function'return) so a future skip isn't silent again. - Consider whether a shipped system dataset whose backing table may not exist
should surface as "not available" in the widget rather than a confident0—
an empty audit chart and a zero-events audit chart mean very different things
to whoever is reading a security dashboard.
Repro
os dev --ui --seed-admin -d file:/tmp/fresh.dbinexamples/app-showcase- Sign in, open
/_console/apps/com.objectstack.setup/dashboard/system_overview - Server log shows the
no such table: sys_audit_logwarnings; the dashboard
shows zeros with no error sqlite3 /tmp/fresh.db ".tables" | grep -E 'sys_audit_log|sys_activity'→ nothing
Observed on main @ 0e96e46, SqlDriver(better-sqlite3), single-tenant.
Found while browser-sweeping showcase + Studio for #4879.
Activity
claude commentedon Aug 4, 2026
认领:PM 循环第 1 轮(identity 车道)
会话:
session_015W6nhsDrz6zWQc8je12a1t分支:
claude/issue-4887-audit-provisioningWorktree:
objectstack-issue-4887域:
domain:identity文件面:
packages/plugins/plugin-audit/**(主);诊断可能读packages/objectql/src/engine.ts但不改 —— 若修复确需改 engine 侧,越界即停,报告说明Generated by Claude Code
claude commentedon Aug 4, 2026
PM 复核(第 1 轮,identity 车道):接受 PR #5035 的在道内范围;本单前提被证伪,公开确认;真因移交 #5033
dev 的报告是教科书式的「先核前提」运行,复核结论逐条:
一、issue 前提证伪 —— 我在派发词里也传递了同一个错误前提,一并更正
本单(以及我的派发词)断言 provisioning 从未发生、怀疑
typeof sync !== 'function'静默提前返回。实测两条都不成立:provisioning 正常执行,表建在了 ADR-0057 §3.6 路由的 telemetry datasource(os dev默认建的兄弟文件dev.telemetry.db)—— 报告者(和我)只看了主库文件。sys_comment无 lifecycle class 留在主库,恰好解释了「为什么它存在」。证据:主库 88 对象无这两张表;fresh.telemetry.db里sys_audit_log[table]、sys_activity[view+轮转分片] 俱在;同一对象 data API 读到 49 条真实审计记录而 analytics dataset 读 0。二、真因与决定
仪表盘读 0 的真因是
service-analytics的executeRawSql桥接丢弃 objectName(engine.execute 的options.object路由永远走不到)。已按 dev 建议裁定 方案 A(修 producer),理由与已知行为变更见 #5033 上的路由评论;该单已打domain:services+pm:queue交 services 车道派发,本车道不跨界。三、PR #5035 验收(在道内范围)
plugin-audit两文件 + changeset,零越界);draft ✓ 指向 main ✓Fixes头 ✓sys_audit_log→telemetry, …与非默认 datasource 提示四、关于合并后本单关闭
保留
Fixes #4887:本单的可执行部分(provisioning 可听化)由 PR 交付,前提部分被证伪,症状的唯一跟踪点收敛到 #5033(一事一单)。看到仪表盘仍为 0 的读者:那是 #5033,不是本单回归。CI 绿后按流程转 ready 入队。
Generated by Claude Code
fix(plugin-audit): report where the audit system tables were provisio…