Skip to content

fix(plugin-audit): 让 provisioning 说出表建到了哪个 datasource (#4887) - #5035

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4887-audit-provisioning
Aug 4, 2026
Merged

fix(plugin-audit): 让 provisioning 说出表建到了哪个 datasource (#4887)#5035
os-zhuang merged 1 commit into
mainfrom
claude/issue-4887-audit-provisioning

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4887

TL;DR —— issue 的表象不成立,真因在别的车道

#4887 报告 "plugin-audit 从不 provision sys_audit_log / sys_activity",并猜测是 provisionSystemTables() 里那个静默的 if (typeof sync !== 'function') return; 提前返回了。

实测下来两条都不成立。 provisioning 是好的,syncObjectSchema 被正常调用、表被正常创建 —— 只是创建在了另一个物理库里。真正的缺陷在 service-analytics 的原始 SQL 桥接,已另开 #5033(未认领,不在本车道)。

本 PR 只做本车道该做、且 issue 明确要求的那件事:把 provisioning 的静默改成可听见,让下一个人不必重走这条弯路。

诊断:两个 sqlite 文件

examples/app-showcase,main @ e001a1f,SqlDriver(better-sqlite3),单租户:

pnpm dev -- --database file:/tmp/os4887/fresh.db -p 39117

启动横幅 Plugins: 47 loaded 里同时有 TelemetryDatasourceAudit。这是关键 —— 磁盘上有两个库:

=== /tmp/os4887/fresh.db ===            主库,88 个对象(与 issue 报的 88 完全吻合)
  sys_audit_log  : MISSING
  sys_activity   : MISSING
  sys_comment    : sys_comment

=== /tmp/os4887/fresh.telemetry.db ===  ADR-0057 §3.6 兄弟文件,9 个对象
  sys_audit_log  : sys_audit_log                              [table]
  sys_activity   : sys_activity, sys_activity__r20260804      [view + 轮转分片]
  sys_comment    : MISSING

原因是 ADR-0057 §3.6(P3 separation,#2791 已实现):sys_audit_loglifecycle.classaudit,sys_activitytelemetry,引擎 getDriver() 第 3 步把这两类对象路由到名为 telemetry 的 datasource;而 os dev 在 file-backed sqlite 主库上默认provision 这个兄弟文件(packages/cli/src/utils/telemetry-datasource.ts:dev.dbdev.telemetry.db)。sys_comment 没有 lifecycle.class,留在主库 —— 所以报告人看到的正是"两个缺、一个在"。

同一个对象,两条读路径给出两个答案:

# 走对象路由(engine.find → getDriver → telemetry 驱动)
GET /api/v1/data/sys_audit_log?$top=200   → HTTP 200,49 条真实审计记录

# 走 dataset 原始 SQL(engine.execute,没带 { object } → 默认驱动)
POST /api/v1/analytics/dataset/query      → HTTP 200,{"rows":[],"fields":[],"totals":[]}
   服务端: WARN [Analytics] … no such table: sys_audit_log

49 条记录真实存在,安全看板显示 0。 这就是 issue 观察到的现象,但成因是 service-analyticsexecuteRawSql 自动桥接拿到了 objectName 却把它丢掉了(packages/services/service-analytics/src/plugin.ts:267),导致 dataset 的原始 SQL 永远打在默认 datasource 上。同一文件里的 executeAggregate 桥接调用 engine.aggregate(objectName, …),路由是对的 —— 两条路径对"这个对象在哪个库"给出了不一致的答案。

详细复现与建议方向见 #5033。那是 packages/services/service-analytics/**,不是本车道,本 PR 不碰。

本 PR 改了什么

syncObjectSchema() 返回 void,并且自己有三个静默出口(对象不在 registry / 没有驱动 / 驱动没有 syncSchema),都不抛错 —— 所以调用方只 catch 异常是分辨不出"建好了"和"什么都没干"的。再叠加本侧那个静默的 typeof 提前返回,结果是:provisioning 全跳过和 provisioning 正常工作,打出来的日志一模一样(都是空)。这正是本 issue 被误诊的机制。

现在 provisioning 会自述:

  • 整体跳过改成 warn,并说清后果 —— 表退回"首次 WRITE 时懒建",先 READ 的环境(首页活动流在任何写入之前就查 sys_activity)会一直打 no such table;
  • 每次启动一行 info,列出每张表落到了哪个 datasource —— 通过引擎自己的 getDriverForObject 解析,不靠推断;
  • ADR-0057 拆分生效时再补一行 info,明确说这些表在另一个 store(SQLite 上就是另一个文件),以及"任何不指名对象去读它们的路径(默认 datasource 上的原始 SQL)会报 no such table,尽管 provisioning 是成功的";
  • 对象解析不到驱动时是 warn —— 这种情况 syncObjectSchema() 不发 DDL 也不抛错,per-object 的 catch 永远不会触发,从引擎外面只有这里能观测到。

真实运行时的输出(本分支,pnpm dev 全新库):

INFO AuditPlugin: system tables provisioned — sys_audit_log→telemetry, sys_activity→telemetry, sys_comment→com.objectstack.driver.sql
INFO AuditPlugin: sys_audit_log→telemetry, sys_activity→telemetry live on a NON-default datasource (ADR-0057 §3.6
     lifecycle-class separation), not on 'com.objectstack.driver.sql'. Their tables exist in that store — on SQLite,
     a different FILE. Anything that reads them without naming the object (raw SQL on the default datasource) will
     report "no such table" even though provisioning succeeded.

行为本身没变:同样 sync 这三个对象,per-object 失败依然互相隔离,没有 on-demand DDL 的引擎依然降级而不是让 start() 失败。

测试

新增 AuditPlugin — provisioning is audible (#4887) 5 个用例,覆盖:整体跳过要 warn 且点名后果、逐对象 datasource 落点上报、全在默认 datasource 时不误报拆分、解析不到驱动要 warn、单个对象 sync 失败不影响其余上报。

pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2
 Test Files  7 passed (7)
      Tests  99 passed (99)

pnpm --filter @objectstack/plugin-audit typecheck   → tsc --noEmit,无输出(通过)
pnpm --filter @objectstack/plugin-audit build       → CJS/ESM/DTS Build success
eslint (改动的两个文件)                              → 无输出(通过)

反向验证(防空转):把 audit-plugin.ts 还原成 main 的版本后重跑,新增 5 个用例全部失败、旧用例全绿 —— 新测试确实在测新行为。

范围之外


🤖 Generated with Claude Code

https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t


Generated by Claude Code

…ned (#4887)

`provisionSystemTables()` said nothing on success and returned silently when
the engine exposed no `syncObjectSchema`, so "provisioned three tables" and
"provisioned nothing" produced byte-identical logs. `syncObjectSchema()` itself
returns `void` with three silent exits of its own (object unregistered / no
driver / driver without `syncSchema`), none of which throw, so the per-object
`catch` could not observe them either.

#4887 is what that silence costs. `sys_audit_log` (`lifecycle.class: 'audit'`)
and `sys_activity` (`lifecycle.class: 'telemetry'`) were reported as never
provisioned because they were absent from the primary SQLite file. They were
provisioned: ADR-0057 §3.6 routes both to the dedicated `telemetry` datasource
whenever one is registered, and `os dev` registers one by default as a sibling
file (`dev.db` -> `dev.telemetry.db`). `sys_comment` carries no lifecycle class,
stays on the primary, and was the one that "existed". Nothing in the log
connected those facts.

Provisioning now reports itself:

- the wholesale skip is a `warn` naming the consequence (tables stay
  lazy-created on first WRITE; a read-first env logs "no such table");
- one `info` line per boot listing where each table landed, resolved through
  the engine's own `getDriverForObject`;
- a second `info` line when the ADR-0057 split is in effect, stating that those
  tables live in another store and that anything reading them without naming
  the object will report "no such table" even though provisioning succeeded;
- an object that resolves to no driver is a `warn` — `syncObjectSchema()`
  issues no DDL in that case and throws nothing, so from outside the engine
  this is the only place it can be observed.

Behaviour is otherwise unchanged: the same three objects are synced, per-object
failures stay isolated, and an engine without on-demand DDL still degrades
rather than failing `start()`.

Refs #4887

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 12:34am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-audit.

4 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx (via @objectstack/plugin-audit)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-audit)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-audit)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-audit)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review August 4, 2026 00:43
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit c5e7bd9 Aug 4, 2026
24 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4887-audit-provisioning branch August 4, 2026 00:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants