Skip to content

fix(audit): only stamp organization_id when the audit table declares it (#1532)#1538

Merged
os-zhuang merged 1 commit into
mainfrom
claude/intelligent-meitner-Ii0if
Jun 2, 2026
Merged

fix(audit): only stamp organization_id when the audit table declares it (#1532)#1538
os-zhuang merged 1 commit into
mainfrom
claude/intelligent-meitner-Ii0if

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Summary

Fixes #1532 — on the SQL (better-sqlite3) driver, every record insert/update/delete triggered an audit write that failed with:

table sys_audit_log has no column named organization_id

The error was caught (Audit write failed), so business mutations still succeeded, but no audit row was ever persisted — a silent compliance/audit-trail gap, plus a failed SQL round-trip on every mutation.

Root cause

The SchemaRegistry's applySystemFields() (packages/objectql/src/registry.ts) only auto-injects the organization_id column when multi-tenant mode is enabled:

const wantTenant = opts.multiTenant && sf?.tenant !== false && !tenancyDisabled;
if (wantTenant && !schema.fields?.organization_id) { /* inject */ }

Neither sys_audit_log nor sys_activity declares organization_id explicitly (they rely on this auto-injection). So on single-tenant stacks the column never exists — yet audit-writers.ts stamped organization_id into every auditRow and activityRow unconditionally, making the INSERT reference a non-existent column.

The reporter's stack is single-tenant, matching the 100%-reproduction.

Fix

The audit writer now resolves each target object's registered field set via engine.getSchema() (cached, since schemas are static after registration) and only includes organization_id when the column actually exists:

  • Multi-tenant — column is auto-injected → organization_id is stamped exactly as before, preserving the RLS tenant predicate (organization_id = current_user.organization_id) so non-admin members still see their audit rows.
  • Single-tenant — column is absent → the key is omitted, so the INSERT is valid and audit rows persist.

tenant_id (an explicitly declared lookup on sys_audit_log) is unaffected.

Tests

Added audit-writers.test.ts covering both paths:

  • single-tenant (column absent → organization_id omitted, tenant_id still written)
  • multi-tenant (column present → organization_id stamped from tenant context)

Both pass. (Note: the pre-existing objects.test.ts can't resolve @objectstack/spec/system in this environment because the spec package isn't built — unrelated to this change.)

https://claude.ai/code/session_01UPMrWD5uBQZ29gQBNZBu5x


Generated by Claude Code

…it (#1532)

On single-tenant stacks the SchemaRegistry's applySystemFields() injects
`organization_id` only when multiTenant is true, so sys_audit_log /
sys_activity have no `organization_id` column. The audit writer stamped it
unconditionally, so every record write triggered an audit INSERT that failed
with "table sys_audit_log has no column named organization_id". The error was
swallowed ("Audit write failed"), leaving audit logging silently
non-functional on the sql (better-sqlite3) driver and paying a failed SQL
round-trip on every mutation.

The writer now resolves each audit/activity object's registered field set via
engine.getSchema() (cached) and only includes `organization_id` when the
column actually exists — preserving the RLS tenant stamp in multi-tenant
deployments while keeping single-tenant writes valid. tenant_id (an explicitly
declared lookup) is unaffected.

Adds regression tests covering both the single-tenant (column absent → omitted)
and multi-tenant (column present → stamped) paths.
@vercel

vercel Bot commented Jun 2, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 2, 2026 10:21am

Request Review

@os-zhuang os-zhuang marked this pull request as ready for review June 2, 2026 10:31
@os-zhuang os-zhuang merged commit 103dab0 into main Jun 2, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

audit: every data write fails to persist sys_audit_log — "table sys_audit_log has no column named organization_id" (sql driver)

2 participants