Pulp Engine Document Rendering
Get started
Release v0.71.0

Release v0.71.0 — SQL Server storage parity

Date: 2026-04-16 Tag: v0.71.0

Summary

SQL Server reaches full feature parity with Postgres across all runtime paths. Multi-tenant mode, scheduling, DLQs, render-usage analytics, and tenant CRUD now work identically on both database backends. File mode remains single-tenant and non-scheduling by construction.

This release also closes two C.0b multi-tenant operational debt items (audit purge cross-tenant iteration, OIDC archived-tenant guard) and fixes a config validation nesting bug that silently accepted SCHEDULE_ENABLED=true in filesystem asset mode.

What shipped

Release A — SQL Server single-tenant feature parity

  • 4 T-SQL migrations (007–010): schedules, schedule_executions, schedule_delivery_dlq, batch_webhook_dlq, render_usage tables with full index parity, UTC defaults, and tenant_id columns from inception.
  • 4 new store implementations: SqlServerScheduleStore, SqlServerScheduleExecutionStore, SqlServerScheduleDeliveryDlqStore, SqlServerBatchWebhookDlqStore, SqlServerRenderUsageStore.
  • claimPending() uses CTE-based atomic UPDATE with READPAST + UPDLOCK + ROWLOCK for lock-contention-safe single-row claiming (pending -> rendering).
  • Monday-aligned week bucketing for render-usage rollups (avoids DATEDIFF(week, ...) Sunday anchor).
  • SCHEDULE_ENABLED=true config gate updated to accept both postgres and sqlserver.

Release B — SQL Server multi-tenant parity

  • SqlServerTenantStore (full ITenantStore port): slug validation, reserved IDs, name trimming, explicit updated_at on every mutation, JSON metadata round-trip.
  • Real TenantStatusCache for SQL Server (queries tenants.archived_at), replacing the single-tenant no-op shim.
  • 16 assertActive guard sites across 5 SQL Server stores, mirroring Postgres exactly.
  • Tenant-prefixed asset filenames for non-default tenants (mirrors Postgres ${tenantId}/${uuidSlug} rule).
  • MULTI_TENANT_ENABLED=true config gate lifted for SQL Server. Only file mode is rejected.
  • TenantValidationError extracted to shared storage/tenant-errors.ts.

C.0b operational cleanup

  • Audit purge cross-tenant: new purgeAllOlderThan(before) method on IAuditEventStore (all 3 backends + instrumented wrapper). Scheduler now purges all tenants on the operational clock, not just 'default'.
  • OIDC archived-tenant guard: assertActive(OIDC_DEFAULT_TENANT, 'oidc_auto_provision') before addUser in the OIDC auto-provision path. Surfaces archive drift at provision time instead of the next mint.

Config validation fix

SCHEDULE_ENABLED storage-mode gate, SCHEDULE_SMTP_* prerequisites, and SCHEDULE_S3_* credential checks were nested inside the if (ASSET_BINARY_STORE === 's3') branch in config.ts. In the default filesystem asset mode, SCHEDULE_ENABLED=true with STORAGE_MODE=file silently parsed and startup skipped the engine. Hoisted to top level with regression tests.

Docs + website sync

  • Current operator docs (README, deployment-guide, api-guide, editor-guide, evaluator-guide, mvp-scope) updated to reflect SQL Server support.
  • Super-admin example fixed: tenantId: "*" -> tenantId: null.
  • Website marketing copy (index.astro, features.astro) updated.
  • openapi.json and SDK typings regenerated.
  • Stale “Postgres-only” comments cleaned across codebase.

Breaking changes

None. All changes are additive or fix previously-broken validation paths.

Residual notes

  • Audit purge default-tenant-only loop is now fixed (purges all tenants).
  • Three lower-severity shared multi-tenant items remain parked (not SQL Server gaps): readiness probe tenant-table check, schedule-engine active metric default-only, startup SVG audit default-only. These affect both backends equally.
  • Historical release notes (v0.65.0–v0.70.0) are left as snapshots and not updated to reflect current behavior.
  • Existing single-tenant SQL Server deployments with flat asset filenames under the default tenant are unaffected — the prefixing rule preserves default flat. Retroactive prefixing is not applied.