Skip to content

plugin-audit never provisions sys_audit_log / sys_activity — Setup "System Overview" audit widgets silently render zeros #4887

Closed

Description

@xuyushun441-sys

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:ready provisioning doesn't reach syncObjectSchema for
    these objects, and make both bail-outs audible (a logger.warn on 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 confident 0 —
    an empty audit chart and a zero-events audit chart mean very different things
    to whoever is reading a security dashboard.

Repro

  1. os dev --ui --seed-admin -d file:/tmp/fresh.db in examples/app-showcase
  2. Sign in, open /_console/apps/com.objectstack.setup/dashboard/system_overview
  3. Server log shows the no such table: sys_audit_log warnings; the dashboard
    shows zeros with no error
  4. 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

  1. claude commented on Aug 4, 2026

    @claude
    Contributor

    认领:PM 循环第 1 轮(identity 车道)
    会话:session_015W6nhsDrz6zWQc8je12a1t
    分支:claude/issue-4887-audit-provisioning
    Worktree:objectstack-issue-4887
    域:domain:identity
    文件面:packages/plugins/plugin-audit/**(主);诊断可能读 packages/objectql/src/engine.ts 但不改 —— 若修复确需改 engine 侧,越界即停,报告说明


    Generated by Claude Code

  2. claude commented on Aug 4, 2026

    @claude
    Contributor

    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 头 ✓
    • 测试证据 ✓:99/99 过;非空洞检查(回滚实现后恰好新增 5 例失败)是加分项;补丁后真实启动日志打出 sys_audit_log→telemetry, … 与非默认 datasource 提示
    • 语义上本 PR 交付的正是本单明说要的两件:两处静默出口变响亮 + 每张表落点可见 —— 未修 headline 症状,也不该在本车道修

    四、关于合并后本单关闭

    保留 Fixes #4887:本单的可执行部分(provisioning 可听化)由 PR 交付,前提部分被证伪,症状的唯一跟踪点收敛到 #5033(一事一单)。看到仪表盘仍为 0 的读者:那是 #5033,不是本单回归。

    CI 绿后按流程转 ready 入队。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions