Skip to content

Documentation

2026 Achievements Log

The completed-achievements log for SveltyCMS 2026 — dated, self-measured engineering milestones that shipped, moved here from the roadmap when done.

10/6/2026
222 min read Edit on GitHub
On this page

A Comparative Analysis of Architecture, Performance, and Enterprise Readiness

Note

Methodology: All SveltyCMS metrics are self-measured via reproducible benchmark suites (bun test tests/benchmarks/). Competitor data is derived from publicly available documentation, CVE databases (NVD, GitHub Advisory DB), and published architecture analyses as of June 2026. We encourage independent verification. This evaluation is prepared in accordance with EU Directive 2006/114/EC on comparative advertising — all comparisons are based on objectively verifiable features.

At a Glance

SveltyCMS is a self-hosted, headless and API-first content management system built on Svelte 5 (runes) and SvelteKit 3, with TypeScript end to end, a native Svelte UI kit (no client-side UI framework dependency), and first-class adapters for SQLite, PostgreSQL, MariaDB and MongoDB. Its 2026 engineering record — dated, reproducible and self-measured — is summarised below; every number names the harness it came from and the host conditions, and none of it is a third-party certification or a global ranking.

Headline results (self-measured, latest recorded runs on 2026-10-10)

Area Result How it is measured
Read-path latency (SQLite) Warm HTTP end-to-end 0.393–0.486 ms (p95 0.481–0.620 ms), 2 042–2 527 RPS; cold, cache-busted 0.458–0.508 ms, 1 957–2 171 RPS — two same-code runs, recorded 2026-09-26 baseline 0.911 ms / 1 093 RPS BENCHMARK_RECORD=1 … --only=truth-latency (SQLite, local host)
Write-path throughput (PG) Warm HTTP concurrent 8c create 2 950 RPS (+220 %, baseline 920 RPS); update 3 678 RPS (+121 %, baseline 1 665 RPS); HTTP seed burst 13 120 docs/s (9.2× gain, 500 docs in 38 ms) tests/benchmarks/competitive-workload-replica.test.ts (PostgreSQL)
Distance to the driver Read findById at 114 %, listPlain at 213 % of bare-HTTP floor; Write create at 47 %, update at 57 % of bare-HTTP floor (and 35 % of raw in-process driver ceiling at 100k rows) scripts/benchmark-matrix/raw-db-ceiling.ts --db=postgresql --scale=100000
Test estate 5 749 unit tests / 625 files in 27.3 s; full 4-DB contract bundle green on SQLite, PostgreSQL, MariaDB and MongoDB bun run test:unit · bun run test:integration · bun run test:matrix
Static gates check (format + lint + dead-class + design-token + shade + lockfile), lint:docs (263 files) and risk:audit (CVE, secret-misuse, slop, backdoor probe) all clean; project diagnostics 0 errors / 0 warnings bun run check · bun run lint:docs · bun run risk:audit

Verified gains (before → after, same harness)

Stream Recorded change
Write Performance PG/MariaDB native socket pipelining (opt-in coalescing === "1"); create 920 → 2 950 RPS (+220 %), update 1 665 → 3 678 RPS (+121 %), seed burst 1 421 → 13 120 docs/s (9.2× speedup)
Driver Parity All 4 DBs synchronized: PG POSTGRES_SYNCHRONOUS_COMMIT, MariaDB skipReturning without RETURNING *, MongoDB MONGO_WRITE_CONCERN + direct doc return on skipReturning, SQLite SQLITE_SYNCHRONOUS=NORMAL
Security Read-lane enforcement points 1 → 5 (publication clamp, endpoint parity, field-level-access exemption, grant epoch, tenant resolution); leak/bypass paths 3 → 0; stale-grant window until TTL → 0
Performance Warm read latency −47 % to −57 %; throughput +87 % to +131 %; cold cache-busted latency −61 % to −64 %
Efficiency Unit-suite wall time for the widget group 21.90 → 3.07 s (−86 %), media 1.73 → 0.86 s (−50 %), utils 4.62 → 3.52 s (−24 %); total unit suite 5 749 tests in 27.3 s
Quality gates Project diagnostics 42 → 0; build-time warning classes (undefined named import, non-handler +server.ts export, alias in the config graph) turned into test failures instead of surprises
Important

How to read these numbers. They are self-assessments produced by this repository’s own harnesses on a single developer host; run-to-run spread on that host is up to ±10 % for single runs and up to 24 % between two identical runs, and every table above names the conditions. CI — not this document — is the source of truth for pass/fail counts. Where a claim carries an attribution caveat or an unmet acceptance criterion, the caveat is stated with the number rather than omitted.

1. Introduction: The Post-Monolithic Era of Content Management

The content management system (CMS) landscape of 2026 is defined by a rigorous bifurcation between legacy monolithic architectures and modern, composable, headless solutions. As enterprises increasingly demand sub-millisecond latency, edge-ready deployment, and “developer-first” ergonomics, the market has opened for frameworks that leverage the compilation-centric performance of next-generation JavaScript libraries.

1.1 The Definition of “State of the Art” in 2026

To accurately assess SveltyCMS, one must establish the baseline for SOTA technology in the current year. The benchmarks for 2026 include:

  • Zero-Runtime Overhead: The shift away from heavy client-side JavaScript bundles toward compiled, lightweight output.

  • Ultra-Fast Developer Feedback: Sub-1.2s Hot Module Replacement (HMR) for content structure changes, a 60% improvement over legacy patterns.

  • Native Svelte 5 UI Library (March 2026): Zero-dependency, native Svelte 5 UI architecture, achieving <150 requests per page and solving the “ESM Storm” overhead.

  • Ultra-High Performance Toolchain: Rust-powered linting and formatting (oxlint/oxfmt).

    • Linting (Oxlint): Project-wide checks in 47ms (1000+ files).

    • Formatting (Oxfmt): Full project formatting in ~2.2s.

    • Production Builds (Vite 7): Fast builds using Vite’s native bundler.

    • Zero-Warning Codebase: Automated and manual audit cleared all 84 legacy warnings.

    • Automated Doc Integrity (March 2026): Native lint-docs.ts utility achieving 100% link integrity and Path Alignment Strategy (filesystem matching) across 158 files.

    • True Database Agnosticism: The ability to run on NoSQL (for scale), SQL (for relations), or Embedded DBs (for edge) without code changes.

  • Edge Compatibility: The ability to deploy logic and content delivery to the network edge (e.g., Cloudflare Workers, Vercel Edge).

  • Type Safety: End-to-end TypeScript integration from the database schema to the frontend component.

  • Infrastructure as Code (IaC): The ability to define content schemas, permissions, and configurations as version-controlled code.

  • Structural Consolidation (5-Pillar Architecture): Moving beyond file-fragmented systems to a unified, 5-pillar reactive core.

  • Enterprise SEO Excellence: Sub-millisecond redirect lookups and dynamic, multi-tenant sitemap generation with automated indexing pings.

  • Typed Component Loaders: Moving beyond brittle string-based component resolution to type-safe, tree-shakeable import() closures.

This report describes self-measured SveltyCMS behavior and publicly documented competitor capabilities as of mid-2026. It does not claim global ranking, external security certification, or same-harness superiority over all commercial CMS products.

2. The Architectural Foundation: SvelteKit and the Compiler Paradigm

2.1 The “No-Runtime” Advantage

SveltyCMS leverages Svelte’s compiler to generate highly optimized vanilla JavaScript during the build process.

  • Middleware pipeline (April 2026, self-measured): Consolidated security handlers and /api fast-paths; documented ~71% reduction in hook overhead on the measured path (~12 µs p95 micro-bench — not full HTTP E2E).
  • API fast-path skips: Omitting locale/theme/content-init on pure API routes saves on the order of tens of µs per request when those hooks would otherwise run (depends on path and cache state).
  • Throughput: REST/dispatcher benches have recorded high thousands–~14k+ RPS on specific SQLite/local scenarios. Not a claim of global product speed leadership; networked adapters and full auth stacks are lower.

2.2 Performance & Security Infrastructure (New)

In March 2026, SveltyCMS implemented several enterprise-grade infrastructure optimizations:

  • Single-Trip Mutations: Standardized .returning() (SQL) and returnDocument: 'after' (Mongo) across all adapters, eliminating redundant SELECT queries and reducing mutation latency by ~40%.

  • Prepared / hot-path SQL (April 2026): Cached prepared statements and related hot-path work on SQLite, PostgreSQL, and MariaDB reduce per-query compile overhead. Sub-ms reads are typical on embedded SQLite under our harness; networked SQL still pays RTT (~0.5–2 ms class on local Docker, 2026-08-04).

  • Memory Stability & Allocation Efficiency (April 2026): Resolved significant allocation overhead in the LocalCMS SDK by consolidating transient caches into a shared static LRU engine. This refactor neutralized a 524 MB/min memory leak, achieving a STABLE rating with < 1MB/min growth under sustained 3,400+ RPS loads.

  • Negative Caching & Bloom Filter Protection: Implemented a high-performance negative caching engine with an in-memory Bloom filter to track missing keys/documents. When a 404 cache miss is recorded, subsequent requests for the same missing key are resolved immediately in the current microtask tick, bypassing the database driver.

    Negative cache / Bloom miss path is orders of magnitude faster than a full DB miss on repeated absent keys (self-measured hot-miss micro-benches). It reduces miss storms; it does not “immunize” the system against all DoS classes (rate limits, body limits, and ops still apply).

  • Zero-Tax LocalCMS SDK Bridge: Hardened the LocalCMS server-to-server bridge to bypass SvelteKit’s HTTP middleware pipelines entirely. Combined with hot-swappable getter structures and microtask batching, this achieves internal query resolution in <0.05ms for server-side loading and internal API interactions with 0% runtime overhead.

  • Enterprise Redis L2 Cache Optimization (October 2026, self-measured):

    • TCP Socket Performance (TCP_NODELAY + Keep-Alive): Eliminated Nagle packet coalescing delays on small JSON payloads (<1.5KB) by pinning noDelay: true and keepAlive: 5000 on the Redis L2 connection pool, suppressing 5–40ms tail latency spikes.
    • Non-Blocking Evictions (UNLINK): Migrated cache invalidations and tag set drops from blocking DEL to asynchronous UNLINK executed inside multi.exec() batches, preventing Redis main-thread stalls during high-concurrency collection updates.
    • Batch mGet Relation Probing: Wired bulk Redis mGet lookups into populate-resolver.ts to resolve missing relational entity IDs in a single round-trip before issuing SQL/NoSQL findMany queries.
    • Zero-Dependency Optionality: Maintained strict architectural optionality — Redis remains 100% optional, seamlessly falling back to in-memory L1 cache with sub-µs hits when disabled.
  • Active Driver Validation (Anti-False Positive): Implemented mandatory SELECT 1 verification in the MariaDB and PostgreSQL adapters. This ensures that the Setup Wizard provides immediate, clear feedback for missing or incorrect credentials, eliminating “lazy connection” false positives found in legacy ORM implementations.

  • Micro-Telemetry: Native per-operation timing (performance.now()) is bubbled up from the DB adapter to the API meta field, providing high-resolution observability without external dependencies.

  • Automated Bottleneck Detection: Heap Stability, Pool Contention & P95/P99 Variance (2026-09-26):

    • Heap Stability Regression Guard (tests/unit/benchmarks/heap-stability.test.ts): Three automated tests that fail fast when a code change causes unbounded heap growth: (1) 1 k-doc transformation burst asserts <25 MB retained delta post-GC; (2) 3-burst asymptotic leak check asserts <15 MB gradient across 500-doc waves — catches commitment-ratchet leaks that only converge over many rounds; (3) 256-item LRU eviction cap asserts <10 MB post-GC after 2,000 inserts of 8 KB payloads, matching the internal BoundedCache contract.
    • Connection Pool & Mutex Contention Test (tests/integration/databases/pool-contention.test.ts): Fires 20 concurrent background-worker-pattern tasks (outbox/audit/status-poll simulation) against 20 concurrent foreground CRUD writes and asserts: zero deadlocks, all 40 operations succeed, sub-50 ms post-contention query latency. Runs on SQLite locally, switches to PostgreSQL in CI via currentDbType().
    • P95/P99 Latency Variance Report (scripts/verify-benchmark-local.ts): generateLatencyVarianceReport(db) grades the last recorded competitive-workload-replica run — rows selected by testFile and grouped by runId, never by filename — using the run’s own recorded p50/p99 percentiles, the sample count behind them, and the absolute tail excess (p99 − p50). Each workload is classified EXCELLENT (<2× tail ratio) / ACCEPTABLE (≤3×) / WATCH (≤4.5×) / HIGH_VARIANCE (>4.5×); a row whose percentiles are absent, or backed by fewer than 100 samples (where “p99” is the maximum), is reported UNMEASURED with the reason instead of an estimated band. Aggregate rows (p50 = 0 ms, e.g. the seed burst) are listed as excluded, not as unmeasured, so --strict stays passable. printLatencyVarianceReport() renders the table (naming the graded run and its age) and latencyVariancePasses() is the strict verdict — a 🔴 tail or an unmeasured row fails it, 🟡/🔵 stay informational. Superseded (see §3.34): an earlier revision reconstructed p50/p99 from avg/p95/cv when the JSON lacked them and graded those estimates, and read ten hardcoded filenames from a metric-naming scheme the harness had stopped writing.
    • Background Worker Stability (src/services/background/background-runtime-env.ts): The standalone background worker was crashing and restarting in production because its esbuild bundle could not resolve $app/env (a SvelteKit virtual module). The fix: a lightweight runtime replacement (browser=false, building=false, dev=NODE_ENV!==production, version=npm_package_version) aliased in vite.config.ts so the worker never tries to load SvelteKit’s virtual module loader. A companion esbuild plugin now strips TypeScript and compiles .svelte.ts rune modules ($state/$derived) inside the bundle so runes never leak into the Node child as undefined globals. Verified in tests/unit/services/background-supervisor.test.ts.
    • RFC 7240 Prefer: return=minimal Fast Path (create + update): handleCollectionCreate and handleCollectionUpdate now detect the Prefer: return=minimal request header (or ?return=minimal query param) and pass skipReturning: true to the adapter, then return a lean {"_id":"..."} response (~70 B) instead of the full merged document (~3.5 KB). Removes the full RETURNING * PostgreSQL round-trip on the happy path for callers that only need confirmation. Measured: update +4.3% RPS paired median on PostgreSQL 5k docs 8c.
    • PostgreSQL Schema Migration Guard (adapter-core.ts): addedColumns Set tracks which columns were actually ADD COLUMNed in the current migration run. The subsequent CREATE INDEX loop now skips columns that already had indexes — eliminates redundant CREATE INDEX IF NOT EXISTS calls on large schemas with many existing materialized fields.
    • Tests: tests/unit/benchmarks/heap-stability.test.ts (3) · tests/integration/databases/pool-contention.test.ts (2) · tests/unit/services/background-supervisor.test.ts (5, extended). Pre-commit gate: 6/6 checks pass in 2 m 2 s.
  • Streamed collection export (September 2026): GET /api/collections/{id}/export streams NDJSON, CSV, or chunked JSON from streamMany (Mongo cursor, PostgreSQL driver stream, SQLite/MariaDB 500-row pages). streamingExportResponse encodes one record at a time with backpressure. Admin import/export downloads the stream URL instead of JSON.parse of the full payload. Tests: tests/unit/utils/export-encode.test.ts. Docs: Collections API.

  • Collection Weak ETags & DB-free 304 (September 2026): GET /api/collections/{id} emits W/"cv1|{tenant}|{collection}|{epoch}|{reprHash}". If-None-Match is compared to the in-memory collection epoch (bumped on write with L1 eviction) in handleTurboGet and handleApiRequests before the collections handler — no DB, no JSON. Representation hash covers path, query, and user id. Tests: tests/unit/services/collection-etag.test.ts. Docs: Collections API.

  • Native Field-Level Encryption at Rest (September 2026): Collection fields flagged encrypt: true are transparently encrypted in write-pipeline.ts and decrypted in read-pipeline.ts using AES-256-GCM envelopes (v1:iv:tag:ciphertext) keyed from ENCRYPTION_KEY with HKDF domain separation (sveltycms-field-at-rest) and AAD binding (tenantId:collectionId:fieldName). Physical adapters store only ciphertext; L1/L2 caches keep ciphertext (decrypt clones on the way out). Filter/sort on encrypted fields fail closed (ENCRYPTED_FIELD_NOT_QUERYABLE). Unique indexes and SQL materialization are skipped. Tests: tests/unit/utils/field-encryption.test.ts, tests/unit/services/field-encryption-pipeline.test.ts. Docs: Field-Level Encryption.

  • Interactive Admin Session Console (September 2026): Shipped a comprehensive user session management modal (modal-user-sessions.svelte and DELETE /api/user/sessions) inside User Management. Administrators can inspect concurrent active sessions per user account, complete with client metadata (OS, browser type, IP address, and current active session indicator) and execute one-click remote session revocation with instantaneous authorization cache invalidation. Tests: tests/unit/auth/sessions/session-manager.test.ts.

  • GDPR Article 17 Right to Erasure & Benchmark Hardening (September 2026): Implemented permanent cascading user erasure (eraseUser) in GDPRService and exposed authenticated action: "erase" and action: "wipe" on /api/gdpr. Cascading deletion purges associated sessions, active tokens, audit logs, and account records with 0 orphans across all 4 database adapters. Migrated tests/benchmarks/right-to-be-forgotten-audit.test.ts from the legacy /api/testing backdoor to the authentic production endpoint with benchmarkAuthHeaders(), unblocking the test to execute inside the production benchmark matrix with <1ms average wipe latency and 900+ wipes/s. Tests: tests/unit/api/gdpr.test.ts, tests/unit/services/gdpr-service.test.ts, tests/benchmarks/right-to-be-forgotten-audit.test.ts. Docs: ADR: Testing 2026.

  • Point-Lookup Slab & Hot-Path Serialization (September 2026):

    • Random-Access Slab: Implemented a dedicated 2,000-slot FIFO cache (randomAccessSlab, 30s TTL) for high-frequency point lookups, elevating findByIdRandom throughput by +196% to match the hot-path findById baseline.
    • LRU Protection: Applied updateAgeOnGet: false on ENTRY and CONTENT lookups, preventing cold random reads from displacing hot cache working sets.
    • Pattern Invalidation Acceleration: Refactored pattern-based cache clearing to reduce invalidation latency by −87% (4.11ms → 0.53ms), achieving +676% RPS in mutation benchmarks.
    • O(N) Fast JSON Serialization: Replaced rope concatenation with pre-allocated buffer joins in serializeArrayFast (fast-json.ts), eliminating GC pressure during listLarge queries.
    • Pool & SMTP Hardening: Elevated hardware-profile connection pool floors to 32 on 8c+ hosts, and hardened transactional SMTP connection pooling (maxRecipients: 1, maxMessages: Infinity, connectionTimeout: 10s, socketTimeout: 30s).
  • Store Architecture Modernization & Svelte 5 Runes Consolidation (September 2026):

    • Lean Store Footprint: Consolidated the global store directory from 28 files down to 18 single-purpose domain singletons.
    • Monolith Elimination: Removed the legacy store.svelte.ts singleton and compatibility bridges in favor of direct imports from specialized modules (locale-store.svelte, validation-store.svelte, toast.svelte).
    • State Absorption: Absorbed satellite stores (status-store, data-change-store, user-prefs-overlay) into cohesive domain singletons (collection-store, theme-store).
    • Domain Colocation: Relocated single-consumer stores (consent-store, settings-state, dashboard-preferences, image-editor-store, help-state) directly into their respective feature and component directories.
    • Widget & Shell Decoupling: Updated all 72 widget inputs, displays, schemas, and app shell routes to import directly from domain singletons, eliminating dozens of unused legacy imports and standardizing on Svelte 5 fine-grained reactivity ($state(), $derived()). Tests: tests/unit/stores/, tests/unit/components/image-editor/.
  • Write-Path Acceleration & Single-Pass Field Preparation (August 2026):

    • Deduplication of Sanitization Passes: Eliminated redundant recursive sanitizeObject tree traversal in prepareWritePayload, relying on the pre-compiled PrepPlan in prepareCollectionFields to perform targeted HTML/text sanitization per schema field.
    • Pre-Compiled Number & DateTime Plans: Pre-compiled DateTime and Numeric constraint plans in SchemaHotFlags (_dateTimeFieldNames, _numberFields), eliminating runtime schema.fields iteration in the hook pipeline and achieving zero field-iteration loops on write mutations.
    • Behavioral Mutation Self-Learning: Integrated recordWriteAccess directly into schedulePostWrite and commerce addCartItem / decrementStock, feeding mutation signals (weight: 2) into the in-memory decaying heat map without blocking response latency.
    • Canonical Keyspace Cache-Warming Alignment: Aligned CacheWarmingService keyspace with CollectionsNamespace (${tenant}:collection:${id}:${entry}:published and ${tenant}:collection:${id}:find:default_50:published), ensuring pre-warmed entries and lists produce 100% cache hits on subsequent SDK reads.
    • PostgreSQL Parameter Coercion: Hardened bindPgParam to coerce undefined to null across all prepared statement queries, eliminating driver parameter rejection.
  • Security Hardening (API & Multi-Tenancy):

    • Universal Tenant Context: Implemented a rigorous middleware-driven tenantId propagation system, ensuring every database mutation and query is explicitly scoped to the correct tenant.

    • IDOR Prevention: Hardened all 40+ API endpoints with role-based access control and explicit owner verification.

    • GraphQL Isolation: Eliminated schema leakage by deploying isolated per-tenant Yoga server instances.

    • Tenant-Aware Uniqueness: Implemented granular control for unique field constraints. The tenantScopedUnique flag ensures that identifiers like slugs (e.g., /about) remain unique within a tenant but can safely overlap across different tenants, preventing cross-tenant namespace collisions.

    • SSE Hygiene: Resolved a critical memory leak in the real-time event stream and implemented tenant-aware real-time filtering.

    • Encrypted Data Portability: Standardized AES-256-GCM encryption for all sensitive fields during tenant-scoped data exports.

    • Automated Security Response (v2026): Implemented a unified SecurityResponseService providing:

      • Structured Payload Scanning: JSON and Form-data aware parsing, avoiding the overhead of raw string scanning on binary/multipart payloads.
      • Bounded ReDoS Protection: All security regex patterns now use strictly bounded quantifiers ({0,500}), effectively eliminating the risk of accidental or malicious Regular Expression Denial of Service.
      • Distributed Redis State: Security incidents, IP throttles, and blocks are distributed via Redis, ensuring immediate global protection across multi-node clusters.
      • Performance Early-Exits: Sub-microsecond Content-Length checks and a 32KB processing cap for unknown payload types ensure high-volume protection without performance degradation.
    • Hardened Authentication Core:

      • OAuth State Integrity: Native HMAC-SHA256 signing and verification of OAuth states, neutralizing authorization code replay attacks and CSRF during external identity provider flows.

      • Compartmentalized Rate Limiter Secrets: Decoupled RateLimiter cryptographic keys from the primary JWT_SECRET_KEY, utilizing a dedicated RATE_LIMIT_SECRET fallback architecture. This containment strategy prevents a leaked rate-limit key from compromising the core authentication perimeter.

      • Strict IP Resolution: Implemented getClientIp(event) across all authentication mutation flows (Login, Sign-Up, Forgot Password), replacing generic “N/A” placeholders in Audit Logs to guarantee high-fidelity forensic traceability.

      • RFC 6585 Compliance: Enforced strict Retry-After: 60 HTTP headers for all 429 Too Many Requests responses generated by the authentication firewall, ensuring respectful client backoff protocols.

      • Setup Wizard Gating (June 2026): Hardened setup route handlers (setup.ts) to query the database and check for registered admin accounts, blocking setup hijacking attempts (403 SETUP_ALREADY_COMPLETE) even if the local config file config/private.ts is deleted or corrupted.

      • Stateless CSRF Exemption (June 2026): Excluded Bearer-authenticated API key and Website token requests from CSRF checks in +server.ts to allow secure external client mutations without blocking browser-based cookie-authenticated sessions.

        • Demo Tenant Capacity Guard: Integrated a hard-coded 100-user capacity limit for demo tenant signups, utilizing high-performance getUserCount adapters to protect the system from automated resource exhaustion attacks.

        • L0 Session & Cache Stability (August 2026): Refactored the InMemorySessionManager to utilize a bounded direct Map with capacity caps (MAX_SESSIONS = 10000) and proactive timestamp eviction, eliminating GC drop hazards while preventing memory leaks. Additionally, added process-local _clientNodesCache to contentStore and _clientSchema memoization, eliminating repetitive layout JSON serializations.

      • TOTP Secret Encryption: TOTP secrets are now encrypted at rest using AES-256-GCM under the instance

      • HKDF + size-aware compression (August 2026): Passphrase-shaped ENCRYPTION_KEY / SECRET_ENCRYPTION_KEY values (not 64-char hex) derive AES-256 keys with HKDF-SHA-256 (cached once per process, domain-separated per subsystem). Decrypt still accepts SHA-256(raw) envelopes. Hex keys are unchanged (AES-NI, no KDF on the request path). Compression negotiation now prefers gzip below 4 KiB and skips zstd below 32 KiB; compressSync refuses output that is not smaller than the input. No ML-KEM on the request path.

      • SQL PK upsert + bulk import (August 2026): crud.upsert / upsertMany on SQLite, PostgreSQL, and MariaDB are one INSERT … ON CONFLICT (_id) / ON DUPLICATE KEY when the filter is a primary-key lookup (status-pinned queries still use findOne). Collection import (sync LocalCMS and the import-data job) shares bulkImportCollectionDocuments: findByIds + insertMany/upsertMany per 100-row chunk.

      • Automation event→flow index (August 2026): Automation dispatch indexes active event-triggered flows by event name so content mutations resolve matching flows with a single Map lookup instead of scanning every flow. Unmatched events exit before flow iteration.

      • QueryBuilder list + Mongo 1-RT upsert (August 2026): Admin collection lists (queryBuilder.execute) convert rows in place with registered schema maps on SQLite/PostgreSQL/MariaDB; exists() is LIMIT 1. Mongo upsert is one findOneAndUpdate (filter carries _id) instead of update-then-create.

      • Plugin state + relation batch (August 2026): Extensions uses the layout plugin-state map (one findMany + 15s L1) instead of N getPluginState; config reuses parent().pluginStates. Relation Input/Display issue one _id $in list request; collection lists hydrate relation labels in one findByIds per related collection so table cells skip extra HTTP.

      • Remotes skip HTTP-to-self (August 2026): User profile, sessions, tokens, settings groups, and collection save/delete remotes call LocalCMS in-process (getRequestLocalCMS) instead of event.fetch("/api/..."). Dashboard health uses cms.db.isConnected() instead of fetching /health. Same privilege strips as the REST handlers.

      • SQLite write templates + GraphQL fast path (August 2026): SQLite INSERT synthesizes the row (no RETURNING *) with a cached SQL template; UPDATE SQL is cached per column-set. GraphQL contentSystemHealth / allCollections skip Yoga/JIT after auth (in-memory catalog/health).

  • TOTP Replay Protection: A consumed-codes registry using an adapter pattern (in-memory, swappable to Redis/SQLite) prevents TOTP code reuse within the 90-second validity window. Fail-closed design — registry errors reject the code.

  • TOTP Timing Attack Mitigation: Replaced standard string comparisons in the 2FA layer with crypto.timingSafeEqual, mathematically neutralizing nanosecond-level side-channel attacks on TOTP code verification.

    • Trusted Devices: HMAC-SHA256 signed __Host-2fa-trusted-device cookie (30-day TTL) enables “Remember this device” — subsequent logins skip 2FA on recognized devices. Forgery-proof design using the instance’s ENCRYPTION_KEY.
  • Plugin Auth Hooks: New afterAuthenticate lifecycle hook allows plugins to deny logins or enforce 2FA gating on any authentication method (password, OAuth, passkey, token, magic link). Fires after credential verification but before session cookie issuance.

  • Pending 2FA Setup: Encrypted TOTP secrets are persisted immediately during setup with twoFactorPending: true — interrupted enrollment is automatically resumed on re-initiation.

  • Configurable TOTP Window: TOTP_WINDOW env var (default 1, max 5) for clock-drift tolerance without code changes.

  • Fallback Garbage Collection Resilience: Hardened the session cleanup loop to guarantee that in-memory fallback caches are continuously pruned even when a RedisSessionManager is actively managing the primary connection, ensuring zero memory leaks during Redis outages.

    • May 2026 AI Bot Defense & Crypto Hardening:

      • AI Bot Detection & Blocking: Proactive User-Agent fingerprinting detects and blocks 28 known AI crawler patterns (GPTBot, Claude, Perplexity, CommonCrawl, Bytespider) and reconnaissance tools (Nmap, SQLMap, Nikto, Burp Suite, Zgrab, Masscan, Nessus).

      • Multi-Layer Honeypot Grid: 45+ decoy routes covering WordPress, Drupal, Joomla, AWS metadata endpoints, config/backup file probes, and common exploit paths. Any probe triggers immediate IP flagging/blacklisting.

      • Honeypot IP Flagging & Response Poisoning: Honeypot probes get an immediate decoy 200 (no 5–15s socket hold — that was a Slowloris/DDoS vector) while the IP is flagged for firewall rejection on the next request. Bots receive fake JSON payloads (phantom users, false config) designed to corrupt scraper datasets rather than revealing real system information.

      • Cross-Origin Isolation Headers: All API responses enforce Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp, and Cross-Origin-Resource-Policy: same-origin, preventing Spectre/Meltdown side-channel attacks.

        • Zero-Bias Token Generation: generateRandomToken now uses rejection sampling to eliminate ~3.125% modulo bias, guaranteeing uniform CSPRNG distribution for session tokens and API keys.
      • Setup Cookie Prefix Consistency: Setup handler now uses consistent __Host- cookie prefix logic, closing the gap between runtime and initialization session cookie naming.

    • June 2026 Comprehensive Security Audit & Hardening: Full-stack codebase audit against dependency stack, security architecture docs, and CVE landscape. Closed 6 gaps:

      • SVG Polyglot Upload Sanitization: Server-side sanitizeSvg() strips scripts, foreignObject, event handlers, javascript:/data: URIs, CDATA, XML PIs, and DOCTYPE from uploaded SVG files.

      • S3 Endpoint SSRF Prevention: validateS3Endpoint() blocks private IPs (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16), cloud metadata hosts, and non-HTTP protocols before S3Client creation.

        • Ollama RAG Prompt Injection Defense: Remote knowledge-base context relocated to user message role with <rag_context> XML delimiters and control-character stripping.
      • GraphQL Introspection Explicit Blocking: NoSchemaIntrospectionCustomRule applied in production, preventing unauthenticated __schema enumeration.

      • Tiptap Link Protocol Hardening: Protocol allowlist ["http", "https", "mailto", "tel"] blocks javascript: and data: URI injection at editor level.

        • Rate Limiter Dependency Hygiene: sveltekit-rate-limiter moved from devDependencies to dependencies to guarantee production build inclusion. All 64 defense-in-depth security regression tests pass. 0 lint errors. 0 type errors.
    • 4-Layer Defense-in-Depth Authorization: Most CMS platforms (Payload, Strapi, Directus) rely on a perimeter security model: authenticate at the gate, then assume internal trust. This creates dangerous gaps where an authenticated user with low privileges can exploit deeper vulnerabilities. SveltyCMS eliminates this assumption through 4-layer zero-trust re-validation:

      • Layer 1 — Middleware: __Host- cookie prefix enforcement (RFC 6265bis), setup completion gating, CSRF protection.

      • Layer 2 — Dispatcher: Fail-closed hasApiPermission() checks; unmapped namespaces return 403 by default.

      • Layer 3 — Handler: Admin verification for system mutations, media:write/media:delete permission checks.

      • Layer 4 — Page Action: Centralized permission guards (e.g., requireCollectionBuilderPermission). Each layer re-validates permissions independently, and the system fails safely if any check fails. This “zero trust within the application” architecture is rare among open-source CMS platforms and makes entire classes of vulnerabilities impossible — a single compromised perimeter layer does not grant full access.

    • ETag Conditional Requests: Implemented HTTP ETag support in the central API dispatcher. Every GET 200 response receives a SHA-256-based ETag header (first 16 hex chars of the response body hash). The dispatcher checks the If-None-Match request header and returns 304 Not Modified with zero body when content has not changed. This reduces bandwidth consumption and server processing for cache-valid clients, with sub-microsecond hash computation overhead per response.

    • API Versioning: Added X-API-Version: 1 header to all API responses. The dispatcher automatically strips /v1/ path prefixes for backward-compatible routing (e.g., /api/v1/collections/posts to /api/collections/posts). This enables future /api/v2/ endpoints without breaking existing clients, while existing /api/ paths continue working unchanged.

    • Vectorized Audit Chaining: Moved Audit Log SHA-256 hashing to a dedicated Node.js Worker Thread pool. This offloads cryptographic overhead from the main event loop, preventing API starvation during high-volume bulk imports and deletions.

    • Middleware AST Pre-compilation: Introduced a compiled “Auth-Path” cache in the SvelteKit handleSetup hook. Pre-evaluating complex AST route bypasses reduced total middleware latency by ~40%.

    • Unified Gatekeeper Consolidation (April 2026): Purged 13 redundant API route directories and consolidated entire REST surface into a single, high-performance [...path] dispatcher. This reduced AST parsing overhead in SvelteKit by 80% and minimized cold-start times for serverless environments.

    • Enterprise “Immunity” Layer (April 2026):

      • Global Security Guard: A top-level wrapper ensuring HSTS, CSP, and X-Frame-Options are injected into every response, including error boundaries and redirects.
      • Self-Healing Load Shedding: Integrated a memory-pressure monitor (90% heap threshold) that automatically rejects mutation traffic with compressed 503 responses, protecting read-availability during spikes.
      • Graceful Shutdown Instrumentation: Instrumented SIGTERM / SIGINT handlers to drain in-flight requests and cleanly disconnect DB/Cache pools, ensuring zero-restart integrity.
      • Timing-Safe Cryptography: Protected CI/CD test bypasses using crypto.timingSafeEqual to neutralize side-channel attacks on administrative bypass secrets.
      • Request Tracing: Automated X-Request-ID and requestId propagation across the entire stack via AsyncLocalStorage and the middleware pipeline.
    • Lazy Relation Hydration: Eliminated Depth-3 query lag on strict SQL engines (PostgreSQL/MariaDB) by utilizing Svelte 5 snippets ({#snippet}) and the IntersectionObserver API. This “Ghost Relation” pattern delays complex sub-graph JOIN execution until the specific relation field natively scrolls into the viewport.

    • L1/L2 cache (v0.0.7+): Hybrid LRU + optional Redis. Hot-path hits bypass the DB driver; LocalCMS / L1-hot internal timings can land in the sub-0.1 ms class in our micro-benches. Percent reductions vs a specific prior baseline are scenario-dependent — reproduce with tests/benchmarks/cache-*.test.ts / LocalCMS benches.

      Furthermore, batched Ghost Relations mathematically eliminate the N+1 query bottlenecks and relational choking that plague competitors like Strapi and Directus under heavy nested population.

    • Surgical Field-Level Caching: Enhanced the Ghost Caching engine with tagMap surgical invalidation. Single-field updates (e.g., viewCount) now trigger targeted purges of specific document keys instead of flushing entire collections, maintaining “Hot L1” status for 99% of unrelated traffic.

    • Distributed Edge Sync & Real-Time Invalidation: Introduced a Redis Pub/Sub invalidation bridge (svelty:cache:invalidation). This ensures sub-millisecond global state consistency across multi-node edge clusters, with immediate local-memory purging upon remote invalidation events.

    • Geographic Read-Replica Awareness: Implemented a context-aware ReplicaPoolManager in the PostgreSQL adapter. By parsing the x-svelty-region header, the CMS automatically routes read traffic to the nearest global database replica (e.g., US users to us-east-1), while keeping all mutations on the primary instance.

      • Optional Edge CDN Invalidation: Integrated a background CdnService for Cloudflare. Automated purge_by_tags calls are fired upon content updates, ensuring global Edge consistency without manual cache management. This service is 100% optional and fails gracefully if credentials are not provided.
    • Hardened Telemetry Protocol: HMAC-SHA256 registration handshakes for lifecycle reporting to make spoofed heartbeats harder (not a formal PKI guarantee).

    • Persistent DoS Protection (April 2026): Implemented stateful rate-limiter persistence using dump() and restore() mechanisms in SecurityResponseService. This ensures that active IP blocks and throttles survive graceful shutdowns and node restarts, providing continuous protection across deployments.

    • SQLite Multi-Tenant Index Optimization (April 2026): Hardened the SQLite schema with compound (tenantId, field) indexes across authUsers, authTokens, and contentNodes. This eliminated table scans for multi-tenant lookups, achieving sub-millisecond query performance (0.000ms - 0.007ms) even under high concurrency.

    • Connection Resilience Hardening (June 2026): Fixed undici pool hangs when rejected HTTP response bodies were left unconsumed (brownout simulations). Chaos-resilience benches pass after consuming failed bodies; re-run tests/benchmarks/chaos-resilience.test.ts for current numbers.

2.2.1 Scaling benches (April 2026, self-measured)

Selected high-load results from our suites (not “zero overhead” as a universal claim):

  • Hot REST/read path: On the order of sub-ms entry retrieval when schema/cache is warm (scenario-dependent).

  • Content scanning: Worker-pool scan of large collection sets measured in sub-ms–low-ms class under our content-scan bench.

  • Bulk ingestion: Order of ~10k entries / ~1s class in migration-scale style benches (machine- and DB-dependent).

  • Revision-heavy docs: Lookup degradation under many revisions constrained by indexing strategy in revision benches (see benchmark ledger for latest %).

  • Write-path span attribution (2026-09-22, self-measured): PROFILE_WRITE=1 spans now cover the whole create/update pipeline (ns:prep:*, ns:beforeSave, ns:persist:adapter-write, ns:persist:outbox, db:ins:*, db:upd:*) with correct span boundaries — the previous ns:sanitize+validate span closed after the DB write, which inflated validation to 1.14 ms and made it look like the dominant write cost. Real split at 1c: ns:persist:adapter-write 0.24 ms (of which the bare statement call is 0.11–0.18 ms), ns:sanitize+validate 0.035 ms, ns:beforeSave 0.013 ms, row normalization 0.009–0.011 ms, widgets/encryption/outbox ≈ 0. Driver choice is not a factor (node:sqlite 37.8 µs vs bun:sqlite 35.4 µs per INSERT in a like-for-like probe).

  • Middleware attribution for the write lane (2026-09-22, self-measured): hooks-performance.test.ts now reports per-scenario pipeline costs, and the measured middleware-only delta between the light turbo lane and the full auth+security lane is +0.01–0.09 ms — i.e. the middleware chain is not where the mutation row’s cost sits (mutation 1.99 ms vs warm reads 0.40–0.47 ms, audit disabled).

  • Rejection-path cost parity (2026-09-22, self-measured, fixed): a 401 used to cost ~2× a valid read in tests/benchmarks/auth-performance.test.ts (every rejected flavour 0.84–1.00 ms vs 0.40–0.46 ms authenticated; SQLite, warm). The root cause was outside the middleware: the production 401/403 fast-path envelopes (src/utils/error-handling.ts) were the only API responses built without a content-length, so handleCompression read them as “unknown size”, skipped both the <1 KiB skip-gate and the buffered tier, and negotiated the streaming tier — a fresh zstd stream per reject (~0.45 ms for a 64-byte body; Accept-Encoding: identity restored the cheap path, which isolated it). The sequence() spans also nest, so “every phase wrapper inflates” was a nesting artifact rather than evidence of a per-stage cost. After declaring the length, the reject rows land at 0.35–0.39 ms — parity with the authenticated rows (0.33–0.36 ms turbo-served, 0.29–0.33 ms full chain; five runs each side, 2026-09-22). The harness now prints a live lane/cache/hook-count table per row, because Auth Validation @ 1c is turbo-cache served (4 hooks) while a rejection runs the full chain (7), and Auth (full chain) @ 1c is the like-for-like authenticated row (10 hooks). Supersedes the earlier “1.85x Fast-Fail Efficiency” figure (pre rate-limit/AMR/WAF consolidation) and the “cause is an open roadmap item” note — see Server Hooks.

  • Monitoring Tax Elimination: Reduced health-probe latency to 0.45ms via intelligent caching, protecting production CPU cycles from monitoring overhead.

  • Cold point-read response audit (2026-09-22, self-measured, fixed + refuted): the “unknown size” trap behind the rejection fix was swept across every hand-built response. Fixed: the API cache-hit path (src/hooks/handle-api-requests.ts serveCachedEntry) deleted its inherited content-length, so every L1/L2 cache hit — 0.1 KB envelope to 250 KB list — was negotiated into the streaming compression tier instead of the <1 KiB skip-gate or the ≤ 64 KiB buffered tier; it now declares the length of the body it actually sends (computed, never the stale cached value), with tests/unit/hooks/api-requests.test.ts pinning the contract. Fixed the same way: the 62 B 503 Database adapter unavailable envelope in handle-local-context.ts. Already compliant (verified, not changed): buildJsonResponse/fastSuccessResponse (computed length), the error envelopes, the rebuilt token-resolution response, /files (declared from the file stat), and every reject from handleSecurity/handleRateLimit/handleTurboPipeline — those return upstream of the compression stage, so no fix applies. The competitive findByIdRandom row itself is not affected by any of this: a warm session’s point read is served by the collection read lane, which returns from handle before the middleware chain and never reaches compression. Its cost was attributed end-to-end instead (lane HYPER_TURBO/MISS, 676 B body + 26 headers = 1279 B on the wire; 10 k-doc replica, 8 c, n = 6790 misses): SDK findById 55 µs (SQLite driver 27 µs, row/date conversion 12 µs, JSON + point-cache insert 25 µs), response construction 69 µs, SDK facade + session/tenant prep 20 µs — against a raw-HTTP floor of 28 µs (raw-db-ceiling.ts --db=sqlite) and ~10 % of server CPU in undici header/body plumbing vs ~4.5 % in all bundled CMS chunks. No lane-local step is provably unnecessary, and none is big enough to close a ~70 µs gap — the row’s headroom is framework/driver/transport, tracked in the roadmap rather than “fixed” with a speculative change. Three fingerprint-pinned runs per side: warm sequential 0.430–0.515 ms before vs 0.356–0.469 ms after (medians 0.451 vs 0.443 ms), warm 8c 2.215–2.273 ms before vs 2.266–2.698 ms after (medians 2.236 vs 2.384 ms) — ±13–20 % machine noise that swallows the difference both ways, as expected for a fix that is off this row’s code path. Details and the per-site table: Server Hooks.

  • Simulated Form Vitality: Proved a sub-0.05ms server prep time for complex 50-field forms, ensuring the Admin UX feels instantaneous regardless of schema complexity.

  • Transparent AI Integration: Reduced CMS-side AI logic tax to 1.3ms, ensuring the platform adds zero perceived latency to generative workflows.

  • High-Throughput Large-Payload & Concurrency Scaling (September 2026):

    • Single-Pass V8 JSON Serialization: Replaced intermediate string template allocation in successResponse with direct V8 streaming serialization, reducing 225 KB payload serialization time from 1.47s to 650ms (55.7% latency reduction) in micro-benchmarks.
    • SIMD-Accelerated ByteLength Calculation: Fast-paths payloads $\ge$ 1 KB to AVX2/SSE4.2 SIMD Buffer.byteLength(), eliminating JavaScript character-by-character loops on large responses.
    • L1 Query Cache Retention: Kept CacheCategory.CONTENT with updateAgeOnGet: true (restricting coldCategory to ENTRY), ensuring high-traffic collection queries and pagination pages stay hot in L1 cache under concurrent workloads.
    • Adaptive Brotli Quality Scaling: Lowered Brotli compression quality for dynamic API responses between 32 KB and 256 KB from quality 6 to 2 (4.4× faster compression with <0.1% ratio difference), preventing threadpool saturation under multi-worker spikes.
    • Hardware-Concurrent Libuv Threadpool: Automatic boot-time scaling of UV_THREADPOOL_SIZE up to 32 worker threads prior to module initialization in the shared server entry (index.server.mjs), on both Node.js and Bun.

2.3 The Typed Widget Revolution (March 2026)

One of the most significant architectural leaps in 2026 was the migration of the “Three Pillars” widget system from string-based paths to Type-Safe Component Loaders.

  • The Problem: Legacy CMS architectures (including early SveltyCMS) relied on strings like inputComponentPath: './MyWidget.svelte'. This prevented IDE refactoring, required giant import.meta.glob scans, and led to runtime “module not found” errors.
  • The Solution: Redesigned the createWidget factory to accept typed ComponentLoader closures: () => import('./input.svelte').
  • Result:
    • Zero Runtime Surprises: Broken paths are caught at compile-time by TypeScript and Svelte Check.
    • Perfect Tree-Shaking: Vite no longer bundles unused widgets; only the components for enabled widgets are loaded.
    • AI Integrity: The jsonRender catalog validates referenced components at load time to fail closed on missing widgets (not a cryptographic proof system).

2.4 Core Utilities Standardization (March 2026)

To support the UI migration and global dynamic content, SveltyCMS standardized its core utility belt:

  • cn: High-performance Tailwind class merging (powered by tailwind-merge and clsx) resolving “ESM Storms” in UI components.
  • pluralize: A dynamic, runtime counterpart to Paraglide JS, leveraging Intl.PluralRules to accurately pluralize Arabic, Russian, and English database content (which cannot be pre-compiled).
  • slugify: Advanced Unicode normalization engine capable of safely trans-literating complex diacritics into URL-safe identifiers.

3. Performance & Competitive Analysis

3.1 Competitive Feature Matrix (2026)

The following matrix evaluates core engineering implementations where SveltyCMS provides a distinct technical advantage over legacy and contemporary competitors.

Feature SveltyCMS (v2026) Strapi 5 Payload 3.0 Directus 11 Sveltia CMS
Core Framework Svelte 5 (Runes) React Next.js / React Vue / Node.js Svelte (SPA)
Architecture Server-First (SSR) Node.js / React Next.js / React Node.js / Vue Git-based (Local)
Multi-Tenancy Native Adapter-Level Paid (Enterprise) Plugin-based Native N/A
Slug Uniqueness Tenant-Scoped (Native) Global Only Global Only Manual Config Locale-Agnostic
AI Architecture Local (Ollama) + Cloud + Client‑Side (LiteRT.js) Cloud Only (Paid) MCP Native (Cloud) Cloud Only (Paid) N/A
Dual-Runtime ✅ Dual-Adapter (Node.js + native Bun.serve) Node.js only Node.js only Node.js only Browser Only
Real-time Sync SSE + Yjs (adapter-node compatible) WebSockets None / Custom WebSockets Git Pull/Push
Feature SveltyCMS (v2026) Strapi 5 Payload 3.0 Directus 11 Sveltia CMS
Cache Repair Smart / Incremental Manual Clear Manual Clear Manual Clear Browser Only
SDK Latency Zero (Local Bridge) 2–5ms (Local HTTP) ~1ms (Direct) 3–8ms (Local HTTP) N/A
Negative Caching Bloom Filter (100,000x) None / DB query None / DB query None / DB query N/A
SEO Redirects 0.92 ms (Sub-MS) ~45 ms (Plugin) ✅ Plugin ⚠️ Basic ⚠️ Basic
Toolchain Vite + oxlint/oxfmt Vite (JS/TS) Vite (JS/TS) Vite (JS/TS) Rolldown/Vite
Feature SveltyCMS (v2026) Strapi 5 Payload 3.0 Directus 11 Sveltia CMS
Scheduled Publishing ✅ Adaptive Job Scheduler ⚠️ Cron Plugin ✅ Job Queue ✅ Flows ❌
Content Versioning ✅ Revisions + Diff + Restore ⚠️ Basic ✅ Versions ✅ Revisions ❌
Per-Field Localization ✅ Record + AI ⚠️ Separate Entries ✅ ✅ ❌
Full-Text Search ⚠️ FTS adapters on all 4 engines (not wired) ⚠️ Plugin ⚠️ Plugin ⚠️ Plugin ❌
Crypto-Chained Audit ✅ SHA-256 Tamper-Evident ❌ ❌ ❌ ❌
Smart CMS Migration ✅ AI-Smart 5-Format Importer ❌ ❌ ❌ ❌
Cold Start UX ✅ Progressive / gated init (self-measured) ⚠️ Spinner ⚠️ Spinner ⚠️ Spinner ✅ Local
Quick-Start Templates ✅ 7 Presets (Website Starter default) ❌ ❌ ❌ ❌
In-Repo Site Frontend ✅ SvelteKit + Svedit at / ❌ (bring your own) ❌ ❌ ⚠️ Git-only
Behavioral Learning ✅ Server-side, zero PII ❌ ❌ ❌ ❌
AI Widget Scaffolder ✅ LLM-ready code generation ❌ ❌ ❌ ❌
Token Security ✅ SHA-256 all adapters ⚠️ ⚠️ ⚠️ ❌

3.2 Competitive Benchmark Analysis (2026)

See Competitive Comparison for the full benchmark matrix — every registered module across all 4 databases under extreme concurrent load (source of truth for the matrix: Benchmarks), with per-adapter throughput, Local SDK, and competitor comparisons (Payload, Directus, Strapi, Drupal, WordPress).

What our benchmarks actually show (not a global ranking)

We do not claim global product speed leadership. Public competitor numbers rarely use the same hardware, dataset, or middleware stack. Below is self-measured behavior only.

Layer Self-measured order of magnitude (2026) Caveat
LocalCMS / in-process SDK Sub-0.1 ms class internal calls when L1-hot Not comparable to another product’s HTTP API RPS
SQLite adapter FIND ONE ~0.05–0.08 ms avg (2026-08-04 re-bench) Embedded; no network RTT
Networked adapter FIND/INSERT ~0.5–2+ ms avg on local Docker (PG/Maria/Mongo) Dominated by loopback RTT + engine
findPage vs list+count ~1.6–5.7× fewer ms on dual-query path (same re-bench) Product API change; call sites must use findPage
Count L1 cache hit ~0.024–0.029 ms all engines count only, not full list JSON
Count estimate (unfiltered) ~3–10× vs exact when mode allows Never used for tenant-scoped exactness

Full tables: Performance Architecture · matrix: Benchmarks · buyer framing: Competitive Comparison.

Understanding Executive Latency Uniformity Across Adapters

It is entirely normal and expected for raw CRUD execution matrices to look nearly identical for individual operations within a single database dialect (e.g., PostgreSQL FIND ONE at ~0.85ms vs UPDATE at ~2.15ms across runs). This occurs because:

  1. Network RTT & Loopback IPC Overhead: Under local containerized testing (Docker), loopback socket traversal and TCP handshake serialization impose a fixed 0.4–0.8ms latency floor regardless of query complexity.
  2. Standardized Drizzle SQL Generation: The unified Drizzle AST compiler emits identical SQL query structures for single-record lookups across all relational adapters.
  3. Hardware Engine Isolation: Below the hooks.server.ts middleware pipeline, raw adapter performance reflects the underlying database engine’s storage locks (e.g. WiredTiger in Mongo, InnoDB in MariaDB, WAL in SQLite).

Reproduce locally:

bun test tests/benchmarks/database-performance.test.ts
BENCHMARK_RECORD=1 bun test tests/benchmarks/database-performance.test.ts
bun run scripts/benchmark-matrix/index.ts --sql

8 database reports available at docs/project/benchmarks/benchmark_<db>.mdx with educational context (Measures/Budget/Code/Why), trend labels, and root cause insights.

Earlier 2026 (condensed archive: June–August)

Condensed 2026-09-27: the headline results, measurements and their conditions are retained in full; only narrative prose was removed. The September entries below (3.15 onwards) remain in full detail.

3.3 June 2026 Enhancement Sprint — Closing the Feature Gap

  • Scheduled publishing (production-ready): adaptive job scheduler (src/services/scheduler.ts) polls every 1-second when jobs are pending and every 30-second when idle, at sub-0.1ms CPU per poll cycle; automatic draft→publish, publish→unpublish and scheduled-delete transitions; failures retry with 2^n-second backoff (max 3 attempts) and every execution gets a crypto-chained audit entry; CRUD via POST/DELETE /api/system-jobs; the right-sidebar “Schedule publication” creates real, cancellable jobs.
  • Per-field content localization: translated fields store all language variants in one document (Record<Locale, string>, e.g. { en: "Hello", de: "Hallo", fr: "Bonjour" }), with an inline [EN ▼] locale switcher, {#key fieldName:locale} scoped remounting, one-click AI translation (caching, rate limiting, audit logging), automatic migration of legacy single-string fields, and per-field per-locale translationProgress.
  • Crypto-chained audit logs (SHA-256): every entry stores previousHash (SHA-256 of the prior entry) and chainHash (SHA-256 of self + previousHash), generated with the CSPRNG globalThis.crypto.subtle.digest('SHA-256') (no Math.random() fallback); verifyChain() detects tampering, deletion or reordering; a new AuditHistory component shows a visual chain indicator with a “Verify Chain” button; the schema was extended across all 4 database adapters; SOC 2, ISO 27001 and GDPR Article 30 ready.
  • Database-native full-text search (implemented, not yet wired as of 2026-09): zero additional npm packages — PostgreSQL tsvector/tsquery with weighted column ranking (title:A, content:B, description:C), MariaDB MATCH...AGAINST IN BOOLEAN MODE with FULLTEXT indexes, SQLite FTS5 virtual tables with BM25 ranking, MongoDB $text with field weights, and ILIKE/LIKE fallback; the live ?search= path still uses schema-driven LIKE/regex matching.
  • Smart AI-Driven Migration Pro v2.1 (June 2026): 40+ CMS platforms through 40 parsers (12 PHP CMS + 4 headless SaaS + SveltyCMS native) plus 9 universal formats (CSV, Markdown, SQL, API, JSON, Airtable, Notion, Firebase, MongoDB); 5-step wizard (?plugin=smart-importer, TransformationTree, SSE /api/migration/import) with auto-scaffold (collection-scaffold.ts, infer-collection.ts); 98 unit tests; background queue at 500+ entries; freemium split of 5 platforms + 9 universal formats free vs 31 additional platforms and enterprise features (rollback, delta, PII, webhooks, audit logs) via marketplace license.
  • Collection Builder quick-start templates: 7 setup presets — Website Starter is the recommended default; five additional content templates ship full schemas (Blog/Editorial, Agency/Portfolio, SaaS Product, Corporate Site, E-commerce, Blank).
  • Website Starter + Svedit (July 2026): svedit@0.12.0 public routes (src/routes/(site)/) cover the SSR homepage (slug: home → /) and catch-all pages; SITE_STARTER_ENABLED defaults to true; CMS form + Svedit JSON saves are free while the Editable Website plugin (€14.99, 14-day trial) gates Live Preview and inline click-to-edit; 15 unit tests plus a site-starter.spec.ts E2E smoke.
  • Cold start & progressive init: handleSystemState waits synchronously for the DB on the first request (no intermediate page or polling), empty collections redirect straight to Collection Builder, and MAINTENANCE/FAILED states render the standard SvelteKit error page.
  • Auth expansion: sck_* API keys stored SHA-256-hashed across all 4 DB adapters (generateApiKey(), createApiKey/revokeApiKey/listApiKeys, GET/POST/DELETE /api/api-keys, apikey:{sha256-b64url} cache tags, invalidateApiKeyAuth() L1/L2 purge, api:api-keys permission); single-use magic links consumed atomically (TOCTOU guard); ephemeral ANONYMOUS_USER guest role with content:read/media:read only and no session or cookie; WebAuthn/passkeys ~75% complete (webauthn-cbor 7 pass, webauthn-attestation 4 pass, pending settings-page UI); 32 auth-expansion tests + 68 defense-in-depth security regression tests — all passing.
  • SSRF IPv6 transition blocking: the egress guard and cloud-storage validator now block IPv4-mapped IPv6 (::ffff:0:0/96, e.g. ::ffff:127.0.0.1), 6to4 (2002::/16), Teredo (2001::/32) and bare IPv6 hosts with zone IDs (::1, fe80::1%eth0), closing paths such as http://[::ffff:127.0.0.1]:27017/ into local services.
  • Binary MIME sniffing for large uploads: files of 5MB and above are now verified via file.slice(0, 2048) before stream processing (smaller files already used sniffMimeType(buffer.subarray(0, 2048))); the ~2KB zero-dependency slim-sniffer.server.ts detects 10+ formats by magic bytes (JPEG, PNG, GIF, WebP, SVG, MP4, WebM, PDF, DOCX) and SVGs additionally pass sanitizeSvg().
  • Email normalization: normalizeEmail() (Unicode NFC normalization, whitespace trimming, case-folding) is applied across all 5 auth adapters, replacing 13 raw .toLowerCase() call sites and closing homoglyph account duplication.
  • Scheduled-publish integrity: a pre-publish scan builds a nodeStatusById status map and inspects relation values matching MongoDB ObjectId patterns (24-char hex), skipping the publish when a referenced entry is still draft; cacheService.invalidateCollection() now runs after each scheduled publish, clearing L1 + L2 so GraphQL publicationFilter results stay fresh.
  • Bulk-update and batch fixes: copyDataWithFreshRowIds() regenerates _dndId, _rowId, uuid and key via crypto.randomUUID() in the SDK bulkUpdate(); MongoDB bulkWrite switched from { ordered: false } to { ordered: true } to preserve write order (negligible performance impact; SQLite already sequential).
  • Dashboard chunk retry: retryDynamicImport() uses baseDelay × 2^attempt backoff (1s, 2s, 4s) with +random(0, 200ms) jitter, maxRetries 3 and baseDelayMs 1000, wired into dashboard widget dynamic imports with toast error UX.
  • Data Operations — 6 API domains (July 2026): replaces the placeholder /api/config_sync endpoint — Configuration Promotion /api/config/* (ConfigService, 1,066 lines), Content Packages /api/content-export/* + /api/content-import/* (ContentPackageService, 1,133 lines; NDJSON streaming; 4 duplicate strategies skip/update/create-copy/fail), Data Migrations /api/migrations/* (MigrationEngine, 984 lines), External Importers /api/importers/*, Backups & Restore /api/backups/* (BackupService, 1,770 lines; SHA-256 manifest integrity + AES-256-GCM encryption), Content Sync /api/content-sync/* (ContentSyncService, 785 lines; disabled by default).
  • Data Operations surface & measured performance (SQLite): fail-closed granular read/write/apply permissions per namespace (unmapped namespaces return 403); 15 new webhook event types; GraphQL 16 types, 4 queries, 12 mutations; 6 lazy-loaded LocalCMS namespaces at 0% middleware tax; all 5 core services use dbAdapter.crud on MongoDB, PostgreSQL, MariaDB and SQLite; 89 new unit tests (config-service 20, config-permissions 24, operation-plan 26, identity-matching 19); 2 new benchmark suites (config-promotion 297 lines / 6 scenarios; content-package 2 scenarios); measured: config status 3.3ms avg, export (100 cols) 5.3ms avg, plan 3.1ms avg, apply 3.6ms avg, content-package export (100 entries) 1.12ms avg, import plan 1.01ms avg, SDK overhead 0.00% — all operations sub-5ms.

3.4 June 2026 All-Phase Performance Optimization Sprint

  • Sprint scope: mid-June 2026, a 19-file multi-phase optimization sprint driven by the 53-script benchmark matrix.
  • Phases R1/R2 — hot path: hook timing (performance.now(), traceSpan, Map writes) gated out of production via HOOK_TIMING_ENABLED (also under BENCHMARK=true); pre-compressed Brotli/gzip cache entries stored on MISS and served directly on HIT; X-Original-Size/X-Compressed-Size/X-Compression-Ratio/X-Compression-Algorithm headers plus compression.* benchmark metrics; bulk cache clears via Set iterators, _isBulkClearing deferred tag cleanup, and a _cdnActive fast-path that skips the dynamic import chain when no Cloudflare credentials are configured, with shared setCompressionHeaders(), negotiateEncoding() and hasNativeCompression() utilities.
  • Phase P1 — audit/turbo: fire-and-forget audit logging (user context captured before await resolve(event), log written in a detached promise); the June 14 “−3.57 ms” audit-overhead reading was retracted as a lane mismatch — the same mutation lane now reads 0.590 ms warm / 0.874 ms p95 (ledger, 2026-09-18) and ~2.0 ms in the 2026-09-22 standalone re-run; __turboAuth skips double audit logging on turbo-served mutations.
  • Phase P2 — cold path: OpenAPI pre-warm (apiSpecService.generateFullSpec() after content init) removes the ~2.2ms cold first-hit on /api/openapi.json; clearLocalL1ByTags switched to batched collection + deferred tag cleanup.
  • Phase P3c/3a — batch relational upserts: setMany replaces N individual SELECT+INSERT/UPDATE loops with one INSERT ... ON CONFLICT DO UPDATE (dialect-specific excluded.* / VALUES()), turning ~80 setup-seeding queries into 1; measured June 14 RECORD runs — relational deep 3.61ms avg / 6.06ms p95 (−88% avg, −86% p95), relational shallow 6.68ms avg / 10.71ms p95 / 146 RPS (−78% avg, −75% p95, +336% RPS); reorders wrapped in single transactions.
  • Phase 3b — trained zstd dictionary removed: 0% improvement over generic Brotli at production quality levels (Brotli-4) and no BROTLI_PARAM_DICTIONARY exposure, so the builder, dictionary files and runtime loading code were deleted.
  • Enterprise scaling layers: PgBouncer/ProxySQL/Nginx documentation with inline copy-paste configs in scaling-layers.mdx, DATABASE_PREPARE control for prepared statements behind transaction-mode poolers, and pooler URL support across PostgreSQL/MariaDB adapters; all layers optional — single-node SQLite + L1 remains valid, with Redis for cross-node invalidation, PgBouncer for Postgres multiplexing and Nginx for TLS/static offload.
  • Measured results table (BENCHMARK_RECORD=1, June 14 2026): pre-fix relational ~30ms N+1 estimates → 3.61ms avg (−88% avg, −86% p95) and 6.68ms avg / 146 RPS (−78% avg, +336% RPS); setup seeding ~80 individual queries → 1 batch (−98% round-trips); mutation + audit 1.90ms avg (2026-06-14); bulk cache clear ~3-5× less allocation; CDN purge ~100× less overhead; OpenAPI cold first-hit ~2.2ms → 0ms — methodology: all gains measured via BENCHMARK_RECORD=1 on Intel i7-13700H, 32GB DDR5, Windows 11, Bun 1.3.14, SQLite WAL mode.
  • June 20 soak test: sustained mixed workload (health checks, collection listing, entry reads, schema resolution) at 4 concurrent workers — 5 minutes in CI, 4+ hours locally — recorded 8,989 total requests, 0 errors, RSS 351 → 312 MB (−39 MB), heap slope −0.3 MB/min (a positive slope above 1.0 MB/min would indicate a leak) and latency drift −0.6 ms/min.
  • June 20 stability proofs: SQLite failover recovery in 78ms (service restored under 100ms after a simulated disconnect); large-payload streaming at 333 MB/s with a STREAMING verdict — 5-10MB uploads stream without full buffering and RSS delta stays under 6× file size; SqlAdapterCore refactor eliminated 1,644 lines (−30.6%) with all 1,152 unit tests passing; 4 security fixes (2 CRITICAL token-hashing, 2 HIGH race conditions) — website and auth tokens now SHA-256 hashed across all adapters with atomic consumeToken.
  • June 20 behavioral learning (active): server-side, tenant-isolated, zero-PII tracking with recordCollectionAccess() under 0.001ms, recordEntryAccess() under 0.001ms, predictNextPath() under 0.05ms and getHotCollections() under 0.05ms, feeding adaptive cache warming, prefetch hints and dashboard reordering.
  • June 20 AI scaffolder & marketplace: one-click installPlugin() from marketplace.sveltycms.com; scaffoldWidget(config) → 3 production-ready files; marketplace REST + License v1 POST /api/v1/license/verify; generated widgets use Svelte 5 runes + Tailwind v4 + WCAG 2.2 AA.
  • August 2026 — Marketplace Phase 2: in-app Extensions → Marketplace tab (GET /api/marketplace) with offline-first merge (local themes and dashboard widget packages appear when marketplace.sveltycms.com is unreachable); widgets ship as kebab-case package folders (widgets/<folder>/<component>.svelte + required widget.json + .mdx, compile-time import.meta.glob discovery, installDashboardWidget()); licensing — free (System Health, CPU, Media Storage), freemium 14-day key-less trial (Audit Log €24.99, Unified Metrics €14.99, Logs €6.99, Smart Importer), paid (SCIM Status €12.99, Editable Website €14.99) — enforced client-side via /api/system/license-status?type=dashboard&id=<id> and server-side via checkExtensionLicense("dashboard", widgetId) → 403 LICENSE_REQUIRED.

3.4.1 LiteRT.js AI Client — Client-Side Browser Inference (July 2026)

  • Client-side browser inference (July 2026): LiteRT.js (Google’s Web AI runtime, announced July 9, 2026) runs entirely in the browser with zero server cost and zero data egress, in a dedicated Web Worker served from /ai/worker with its own relaxed CSP — admin headers (COOP: same-origin, COEP: require-corp, frame-src 'none') stay untouched and the worker has no DOM or cookie access.
  • Runtime & fallback: LiteRT.js WASM (~15 MB) with WebGPU primary and XNNPACK CPU fallback; transparent server-side Ollama (llava / nomic-embed-text) when WebGPU is unavailable; lazy WASM download on first inference, not at page boot; first use case is alt-text generation for WCAG/ATAG compliance via ai.generateAltText(image).
  • Measured performance: MobileNetV3 (4.5 MB) alt-text via WebGPU completes in ~150-200ms on a 2024 MacBook Pro M4; EmbeddingGemma (18 MB) generates 384-dimensional vectors in ~300-400ms; both fall back to CPU (XNNPACK) at ~2× the latency; sub-200ms is a target for small tasks, not a guarantee.

3.5 June 23 Content & Media Architecture Consolidation

  • Content system: 16 files → 9 files (~200 lines removed) with 8 scattered modules consolidated into engine.server.ts (scanner + reconciliation + cache + watcher + CRUD) and loader.server.ts (path security + native/pooled schema loading + worker pool); one shared scanCompiledCollections(), a unified refreshContent({ mode: "full" | "schemas" | "incremental" }) replacing 3 scattered refresh paths and a single ensureContentInitialized() coordinator; ~75 lines of dead code plus ~90 duplicated contentSystemBase lines removed; 20 new reconciliation unit tests; benchmark-verified: content scan 0.008ms avg (150 files), stress scan 0.044ms warm (1,000 files).
  • Media system: 17 files → 14 files (~65 duplicate lines removed); 3 duplicate modules merged into media-utils.ts (MIME table expanded from 30 to 65 types with reverse getExtensionFromMimeType() lookup); sharing.ts wired through createLink()/validateLink(); 2KB zero-dependency slim-sniffer.server.ts MIME fallback; 4 DAM endpoints (bulk download, analytics, version list, version compare); real chunk-based parseMultipartStream for multi-GB uploads; inline metadata editing in the media drawer.
  • Media tests & benchmarks: 40 new unit tests (sharing 13, media-utils 15, slim-sniffer 12); upload 10MB at 32.3ms (310 MB/s); SDK processing 2.7ms (319 images/s).
  • Focal Point & Aspect Preview (July 2026): CmsMediaMetadata.focalPoint percentage { x, y } persisted per image via metadata-only PATCH /api/media/:id; CSS-only preview grid across 7 preset ratios (16:9, 3:2, 4:3, 1:1, 2:3, 9:16, 21:9); crosshair drag with 1px arrow-key steps and 10px Shift+Arrow steps; opt-in focal-point plugin (disabled by default) injects into media_gallery, image_editor_tool and media-upload zones; comparable to Drupal’s Focal Point module and Payload CMS’s aspect-preview plugin, which ship as optional extensions there.

3.6 July 16 Media System Overhaul — Security, Types & Streaming

  • Security hardening (7 critical vulnerabilities; 3 CRITICAL + 12 HIGH fixes): path traversal in LocalStorageAdapter.getMetadata() and bulk-download.ts (resolve() + startsWith() / safeResolve() gates), SVG injection via watermark/annotation attributes (SAFE_COLOR / SAFE_NUMBER + guardAttr()), signed-URL timing oracle (constant-time rejection on invalid hex), tenantId pipe injection (replace(/\|/g, "_")), MIME confusion from bare SVG bytes, binary corruption in streaming uploads (byte-level state machine), upload memory exhaustion (incremental parser + backpressure), secrets moved from process.env to getPublicSettingSync(), Cloudinary safeFetch() + validateEgressUrl() + timeout, dead EXIF parsing replaced by binary tag extraction, Windows temp-file leak fixed with cross-platform finally { unlinkSync() } cleanup.
  • Type safety & architecture: MediaItem discriminated union of 5 variants (MediaImage | MediaVideo | MediaAudio | MediaDocument | MediaRemoteVideo) with 7 type guards (isMediaImage(), isStoredMedia(), isMediaOfType(), assertNever()); NewMedia<T> blocks server-assigned fields (_id, hash, url, createdBy) at compile time and MediaPatch restricts PATCH to user-editable fields; typed SharpPipeline; dispose()? on StorageAdapter; as const value maps; normalizeWatermarkPosition() alias normalization.
  • Streaming upload parser: byte-level state machine (never buffers the full body), binary-safe Uint8Array chunks, per-file backpressure; configurable maxFileSize (1 GiB), maxTotalSize (5 GiB), allowedMimePattern and per-chunk read timeout (300s); per the 2026-09-18 update the route now passes explicit caps both set to API_MAX_BODY_SIZE_BYTES (15 MB) and oversized uploads abort with 413 PAYLOAD_TOO_LARGE instead of buffering toward those defaults.
  • Media reference reverse-index: MediaReferenceIndex replaces the O(n × entries) getMediaReferences() scan with an O(1) in-memory lookup (rebuild() lazy on first query, eventBus-driven invalidation on content mutations); estimated 50x speedup on repeated queries, pending benchmark verification.
  • MIME sniffer enhancements: TIFF (little- and big-endian), AVIF/HEIC via ftyp box brand parsing, ZIP sub-type detection splitting DOCX/XLSX from plain ZIP archives, and a hardened WebP 12-byte bound check.
  • Measured impact: type guards 0 → 7 + assertNever(); as any casts ~20 → ~1 (BaseEntity → entry); path traversal vectors 3 → 0; upload data integrity corrupted → binary-safe; reference scan O(n × entries) → O(1) after lazy rebuild; upload memory full-file-in-RAM → proportional to network speed; temp-file cleanup Windows-leaked → cross-platform.

3.7 July 25 Security Hardening & OIDC Logout

  • RBAC permission cache hardening: invalidatePermissionCache() was defined but never called, so stale DENY results survived up to 5 minutes (PERMISSION_CACHE_TTL); it now runs on every Auth.updateUser() mutation and globally from AuthNamespace.updateRoles(), with the turbo auth cache cleared via invalidateTurboAuthForUser().
  • OIDC logout (all 3 mechanisms): RP-Initiated (GET|POST /api/auth/oidc-logout; id_token_hint, allowlisted post_logout_redirect_uri, state; end_session_endpoint auto-discovered via .well-known/openid-configuration with a 1-hour cache), Front-Channel (GET /api/auth/frontchannel-logout; returns 200 with Cache-Control: no-cache and clears SSO sessions), Back-Channel (POST /api/auth/backchannel-logout; validates logout_token claims iss/events/sub/sid, rejects with 400); multi-provider registry registerSsoProvider() + loadSsoProvidersFromSettings().
  • Path traversal defense & media bulk delete: engine.server.ts scanner adds a path.resolve() prefix check alongside the string guards, and config-state.ts validates the resolved path before dynamic import; media bulk delete uses 4 parallel requests (N/4 round trips, ~4× faster) with live progress (Deleting 3/12...) and publish-state gating.
  • Toast hardening, scanners & open item: toasts use restricted ALLOWED_TAGS/ALLOWED_ATTR (mitigating CVE-2026-65902); validateRequiredFields() remains unit-tested but uncalled, tracked in the roadmap; security-audit.ts --full runs OWASP + secret misuse + slop scans, and slop-scanner adds invalid Button variant detection (destructive→error autofix), @apply misuse and goto() anti-pattern checks.

3.8 August 2026 — Session, RBAC & Extension Security Sprint

  • Shipped (self-verified, next as of 2026-08-04): SESSION_DEVICE_POLICY (single-per-device default / single-per-user / allow-multiple) enforced at the createSession chokepoint; absolute SESSION_TTL_HOURS + zero-query sliding SESSION_IDLE_HOURS; block/unblock/delete purging every session layer with permission, roles-list and turbo-auth caches invalidated on every mutation path; credential-free session snapshots; step-up re-auth for cross-session revoke (stateless HMAC, 5-min, session-bound); log-only IP/UA anomaly flagged once per session per hour; SESSION_MAX_PER_USER LRU eviction; expired rows purged every 5 minutes by a scheduled job.
  • Measured cost (self-measured, 2026-08-04, Intel i7-13700H, SQLite): the full security + auth pipeline (HTTP E2E, all hooks) averaged 0.881 ms over 7 recorded runs vs a 0.856 ms July baseline — +0.025 ms (~3%) for the whole session/RBAC stack, with static-asset and turbo baselines at parity; earlier +25% readings were traced to host CPU power-saving mode and re-measured at parity. Summary view: fail-closed authorization at the API dispatcher (unmapped namespaces return 403) plus the cache hierarchy adds ~0.03 ms to the full HTTP pipeline, amortized across cache layers, single-flight coalescing and zero-query design.

3.9 August 2026 — Middleware, Data-Layer & Core Hardening Sprint

  • Middleware hardening (2026-08-04 → 2026-08-10): the 5–15s socket-holding honeypot tarpit became an instant decoy 200 + blockIp() flagging; SSR cross-request state pollution fixed via request-scoped event.locals; bounded rate-limit buckets (evict-oldest, “fixed window with adaptive pressure”); GET request coalescing; in-process mutation prewarm; GraphQL + settings-service pre-warm with parallel Promise.all hook loading; crash-safe 404-log flush + open-redirect guard; module-level settings cache and de-duplicated dbAdapter init; test isolation fail-closed (503 + 10s timeout); Local SDK boot fast-path; 18-hook READY pipeline with no per-request wrapper hops; WebSocket shutdown sends 1001 Going Away on SIGTERM/SIGINT; field-level read filtering via cached FIELD_PERMISSIONS (write-guard still on the roadmap).
  • Data-layer parity: crud.findPage (limit+1 hasMore, optional total, keyset cursor), CountMode (exact | estimate | auto), L1 count cache (30s) plus unit/integration contracts — equal on SQLite, PostgreSQL, MariaDB and MongoDB, and adopted by the collection list path, status facets, users list, media getByFolder, website tokens, Smart Table saved views and the db.<collection>.findPage(...) schema proxy.
  • Non-blocking write core: create/update persist in sub-5ms (~4.20ms) and return 200 immediately; async audit/revision queues (SHA-256 Merkle-tree chaining, revision snapshots, L2 pattern purges) behind AUDIT_CHAIN_SYNC=false default, with true providing synchronous SHA-256 chaining inside write transactions for ISO 27001 / SOC2-style compliance (System Settings toggle); targeted writes on all 4 engines; single session⋈user JOIN cold path with 0 warm DB round-trips; zero boot settling delay; BENCHMARK is outbound-only.
  • Measured impact (self-measured, 2026-08-10, Intel i7-13700H, SQLite, standalone harness): Full Security + Auth Pipeline (all hooks, HTTP E2E) averaged 0.583 ms (p95 0.768 ms, ~1,442 RPS) — ~32% latency reduction vs the 0.856 ms July 31 baseline, with ~40% higher throughput; Turbo Pipeline (light) 0.285 ms / ~3,201 RPS; REST with API caching 0.540 ms; Static Asset 0.549 ms; Mutation + Audit Logging 1.031 ms; single-run variance ~±10%.
  • Superseded (2026-08-23 → 2026-09-18): the 0.583 ms datapoint is not the current value — the August re-baseline moved the same scenario to 0.457 ms, and the SQLite ledger HOOKS_TRACE row now records 0.358 ms warm / 0.532 ms p95 / 2,481 RPS (self-measured, single host).

3.10 August 2026 — ReDoS-safe linear threat scan

  • Linear scanner: Layer 0 WAF and AuthGuard payload analysis share one O(n) scanner (src/services/security/threat-scan.ts); per-request RegExp engines, the unused WASM instantiate stub and new URL() on the allow path are gone; pathological select/from bait returns in well under 25 ms (unit-tested); clean ASCII URLs skip decodeURIComponent; SQL prepareValues uses a char-code ISO datetime prefix.
  • Self-measured 2026-08-23: the security-audit WAF Deep Analysis row stays at 0.002 ms because it times Request.clone() + await analyzeRequest() (a ~1.5 µs floor hiding the matcher); isolated microbench (200k iters, clean /api/collections/posts?limit=10): previous regex inspect 1,495 ns/op / 669,119 ops/s → linear inspectRequest 96 ns/op / 10,388,153 ops/s → isCleanRequestSurface 61 ns/op / 16,442,771 ops/s, i.e. 15.5× less work on the allow path (ReDoS-safe).

3.11 August 2026 — Entry list / field-editor widget + plugin path

  • Widget/plugin path fixes: widget resolution survives a registry not yet ready at onMount, field.widget names resolve, plugin entry_edit slots respect slotRegistry.version, plugin list columns skip disabled plugins, plugin cells use a warm L1 peek and plugin tabs load when selected.
  • Self-measured 2026-08-23 (entry-edit-hydration.test.ts): 50-field loader resolve 0.053 → 0.023 ms (~16.3k → 31.9k RPS, ~2×); widget prefetch 0.013 → 0.009 ms.

3.12 August 2026 — Layout user / plugin / collection-order fetch tax

  • Fetch tax removed: every admin navigation re-read the user from the DB, ran a findOne per slotted plugin and parsed the collection-order manifest from disk, while the settings TTL was passed as 30_000 into cacheService.set (seconds) although the comment said 30s L1; now 15s L1 covers the user snapshot, user count and plugin enablement, one findMany covers all plugin states, collection order is mtime-cached, widget first-load no longer busts /api/widgets/active?refresh=true, profile /user shares the cache and attribute writes/plugin toggles invalidate it.

3.13 August 2026 — Auth-path logs + TOTP backup HMAC (hooks re-bench)

  • Auth-path fixes: debug logs behind logger.isEnabled("debug") and expiry checks via .getTime() < Date.now(); getSmartFirstCollection memoized on contentVersion; cache-lock owner via crypto.randomUUID() only; TOTP backup codes HMAC-SHA-256 (backupcode-hmac: + JWT secret) with the legacy plain SHA-256 scheme removed in 2026-08.
  • Self-measured 2026-08-23 (SQLite, two consecutive hooks-performance runs vs 0.598 ms baseline): Full Auth Pipeline avg 0.457 ms (0.474 first re-run), −23.6% / −20.7%; p95 0.829 → 0.698 / 0.617 ms, −16% / −26%; Auth Overhead (Turbo→Full) 0.276 → 0.143 ms, −48%; Auth @1c avg 0.396 → 0.369 ms, −6.8%; Pipeline @8c 1.244 ms / 3,708 RPS → 1.192 ms / 3,872 RPS, −4.2% / +4.4%; truth-latency HTTP E2E 0.505 / 0.813 ms → 0.509 / 0.710 ms (avg within noise, p95 −12.7%); security-audit pass with no regression. Published 7-run overlay: Full Auth 0.457 ms, stable at 0.474 ms; single-run variance ~±10%; the −20% middleware move is the two-run confirmation against 0.598 ms.

3.14 August 2026 — Hardware-adaptive tuning + honest 4-DB benchmark matrix

  • Hardware-adaptive profile (@utils/hardware-profile): one detection at boot (initHardwareProfile() in hooks.server.ts) published as a frozen shared object; global HARDWARE_CPU_BUDGET default 0.75 (1 = dedicated app server); media/sharp up to 50% of the physical budget, DB fan-out 2 queries per budget core, compile 75% / jobs 50% / module workers 50% (capped); per-knob overrides SHARP_CONCURRENCY, DB_POOL_SIZE, MONGO_MIN_POOL_SIZE, MODULE_WORKER_POOL_SIZE, COMPILE_CONCURRENCY, JOB_CONCURRENCY, UV_THREADPOOL_SIZE; wired into all 4 DB adapters (pool defaults previously hardcoded 20/100/100), the loader.server.ts worker pool, compile + sync-content-state, job queue, compression (weak boxes cap gzip 4 / brotli 4), resilience prewarm, setup wizard and dashboard.
  • Media pipeline (validated on matrix): preset (width, format) dedup collapses 20 variant jobs into 12 unique files; responsive-variant generation for buffer uploads is deferred (fire-and-forget); self-measured 2026-08-23 — SDK media 210.0 → 116.3 ms (−45%), HTTP upload 208.4 → 105.2 ms (−50%).
  • CacheService pattern invalidation: tenant:global:<flat-key> now lives under the tenant:global bucket so pattern purges hit one bucket instead of every key; self-measured 26 → 4.5 ms.
  • Benchmark matrix (2026-08-23): 200/200 green on the hardware-profile build (SQLite 50/0 · MariaDB 50/0 · Postgres 50/0 · Mongo 50/0; run 2 was 199/200, the lone dev-dependency-load failure being auto-generated benchmark MDX drift, now oxfmt-formatted on write); seeder provisions bench_index_pressure + bench_migration_large (fixing the 100k-row index audit failure); restart overhead cut ~20% (only graphql-stress → relational-performance and large-payload-streaming → migration-scale need a restart; the other 5 destructive tests skip the ~12–16s boot, saving ~4 min of the ~22 min run).
  • Pre-profile vs profile (run 2 → run 3, same machine): no systematic regression — the feared GQL-100c pool regression did not materialize (flat-to-better on all 4 DBs), media SDK −3%, with Postgres gains widget pipeline −44%, Yjs −37%, mixed workload −25%, SEO −17–27% and Mongo gains collection search −29%, uploads −17–20%; remaining flags are sub-5 ms run-to-run noise.
  • Reproduce: bun run scripts/benchmark-matrix/index.ts --continue-on-error (4 DBs, ~23 min); bun run test:unit (416 files / 3667 tests); GPU note — sharp/libvips exposes no GPU backend, so media parallelism stays CPU-tier capped and further gains come from job dedup/deferral.

3.15 September 2026 — Intelligence consumption + request-pipeline lean

Two follow-on sprints on top of the 3.14 profile build: the behavioral-learning engine stopped being dormant (renders predictions and heat), and the request pipeline lost its duplicate cache namespaces, duplicate compression, and per-SSR graph cloning.

Intelligence consumption — predictNextPath was computed per request and rendered as a single <link rel=prefetch>; now it renders: (1) the sidebar nav link matching the server-predicted next path is tagged data-preload="smart" (src/components/left-sidebar.svelte); (2) the dashboard consumes the hotCollections already returned by its server load (getHotCollections(tenant, 20)) as compact quick-link chips above the widget grid; (3) heat-aware SWR in collection-service.ts: behaviorally-hot collections (top-20, hot set memoized 10 s per tenant so the per-request cost is one Set.has()) keep the 60 s fresh TTL but serve stale up to 15 min while revalidating; cold collections keep the default 5-min stale window. Roadmap follow-up kept honest: Smart Table “warm next page” viewport preload is still pending — pagination next/prev controls are button-driven today, so the data-preload viewport hook needs the controls converted to real links first.

Request-pipeline lean (hooks + dispatcher): (1) one canonical L2 key — handle-api-requests.ts and the API dispatcher now share a single buildUserCacheKey ({path}?search:u:{user}) for the L2 body store; the duplicate api:{tenant}:{user}:{path} namespace (written/read only inside that one hook) was retired and invalidation clears the shared path prefix across users/searches; (2) single compression per body — the dispatcher marks its tagged L1 writes with a compressed: {} sentinel so response-cache’s internal br/gzip precompute never runs for a write the request hook replaces within the same request; the hook’s one fire-and-forget compressor (br+gzip+zstd, size-gated, genuinely async above the sync threshold) then re-sets the L1 entry with real variants and writes the single canonical L2 entry; (3) nav graph serialized once per (tenant, contentVersion) — the admin sidebar payload was rebuilt + recursively deep-cloned (stripNonSerializable) on every SSR; it is now cached at layout:contentStructure:{tid}:{version} and the root layout’s progressive tree at navigation:tree:progressive:{tid}:{version}, invalidated by the existing content-version signal; (4) one session-cookie resolution per request — authn/authz/turbo/dispatcher consumers now share one per-request slot resolved from the protocol-derived secure-context flag instead of re-parsing the cookie 4–5×. Deliberately NOT merged: the authz 1 h user-count cache (tenant-scope semantics differ — bypassTenantCheck vs tenant-null counts) and the layout’s 15 s fresh user read (avatar/role badge freshness is intentional).

Verified 2026-09-02 (SQLite): bun run check 0/0 · test:unit 453 files / 3,894 tests · test:security 262 · risk:audit 5/5 · test:integration 632 pass / 0 fail (19 adapter skips) · core benchmarks all green (incl. cache-performance, hooks-performance, rest-api-performance).

3.16 September 2026 — Limiter, tenant scope, Mongo REST, Yjs ACL

Follow-on to the dual-path collapse (one limiter, one CPU sampler, branded systemScope). Self-measured core suite on all four engines (console-only, no BENCHMARK_RECORD): SQLite FIND ONE ~0.005 ms / REST ~0.73 ms; Mongo FIND ONE ~0.54 ms / REST ~2.1 ms (HTTP/JSON still dominates the REST delta). Postgres/MariaDB INSERT ~1.7 ms on this host is the Docker Desktop TCP hop — unix sockets apply on Linux/macOS, not Windows.

  • Dead Lua copy removed from handle-rate-limit.ts. Mutation traffic uses rateLimit() (RedisRateLimitStore + MemoryRateLimitStore). Atomic Redis Lua remains in @utils/rate-limit/redis-client.ts only. Tests: tests/unit/hooks/rate-limit.test.ts, tests/unit/rate-limit/engine-fallback.test.ts.
  • bypassTenantCheck boolean is not a query option. Adapters forward branded systemScope from withSystemScope(reason). hasTenantBypass accepts only that brand or bypassSafeQuery. Tests: tests/unit/security/system-tenant-scope.test.ts, tests/unit/security/tenant-isolation-matrix.test.ts.
  • Mongo REST point-read uses native collection.findOne, SDK findById passes skipMeta, and BSON ObjectId _id is stringified before JSON. Same-host: src/databases/db-local-socket.ts (Postgres/Maria unix socket auto-detect; Mongo loopback directConnection, no wire compressors). Tests: tests/unit/databases/db-local-socket.test.ts.
  • Yjs entry ACL — GET/POST /api/collaboration/yjs require docId=entry:{collectionId}:{entryId} and collection:read / collection:write (collection-scoped, then global). ACL helpers sit in yjs-access.ts rather than +server.ts (SvelteKit route analysis rejects non-handler endpoint exports; the production build failed on that). Tests: tests/unit/api/yjs-acl.test.ts.
  • Client-safe tenant split — isMultiTenantEnabled() / withTenant() moved to src/utils/tenant-isolation.server.ts, because MULTI_TENANT is a private key and @utils/tenant is imported by browser code (Yjs provider, SDK config page). isValidTenantId() lives in src/utils/tenant.server.ts, which keeps vite.config.ts’s alias-free config graph intact (compilation/compile.ts imports it directly). globalThis.require is gone end-to-end.
  • Session AMR / MFA persistence — auth_sessions gained amr (JSON/JSONB) and mfaVerifiedAt (tsCol(3) → DATETIME(3) on MariaDB, TIMESTAMP WITH TIME ZONE on PostgreSQL, INTEGER ms on SQLite) across the schema spec, all three Drizzle schemas and the boot-time legacy-tail ALTERs, so existing installs upgrade on next boot. Mongo’s SessionSchema now declares both — strict mode used to strip them, so no engine persisted AMR before this. Session-id rotation carries the MFA proof and device metadata instead of dropping them, and Mongo rotation degrades to sequential writes when the deployment has no transactions (standalone/CI) instead of throwing out of startSession(). Tests: tests/integration/databases/auth-session-amr-contract.test.ts (all four engines). Cost guard: tests/benchmarks/auth-session-amr.test.ts — INSERT Δ(amr vs plain) −14.6 % SQLite / −9.5 % PostgreSQL, i.e. below the noise floor; validateSession (the per-request path) never reads the session columns.
  • Adaptive smart rate-limiting & load shedding — EWMA system pressure load shedding (system-pressure.ts, polling process CPU delta and container loadavg), burst velocity penalization (request-velocity.ts), route-aware token pricing (endpoint-cost.ts), and dynamic user tier and tenant plan quotas (role-tiers.ts, tenant-plan.ts) unified under handle-rate-limit.ts. Tests: tests/unit/rate-limit/.
  • Vitest configuration & test suite hygiene — explicitly configured isolate: true in vitest.config.ts silencing misleading module-evaluation diagnostic hints while strictly preventing cross-file mock and singleton state contamination. Applied silent: "passed-only" locally to eliminate console clutter. Full suite: 504 files / 4,303 tests pass in ~47s.

3.17 September 2026 — Authorization guardrails, APQ binding, 4-DB pagination fixes

A security + performance wave that closed two authorization bypasses, hardened the persisted-query cache and the request-size surface, and found two long-standing product bugs on the database layer. Verified locally: bun run test:unit 515 files / 4499 tests · SQLite integration 660 tests / 71 files (641 pass / 19 adapter skips) · the 7-file cross-engine contract bundle 58/58 on SQLite, PostgreSQL, MariaDB and MongoDB · tsc --noEmit 0 errors · risk:audit 5/5 · bun run lint:docs 0 errors.

3.18 September 2026 — Smart Table platform & multi-core test acceleration

Platform deliverables and high-performance test suite optimization wave:

  • Smart Table column resize & layout namespaces — setLayoutKey(key: string) on SmartTableApi<T> with persistence namespace separation; integrated ColumnResizeHandle into media gallery, users, and website-tokens tables. Pinned columns styled with solid bg-white dark:bg-surface-900 backgrounds to prevent content bleed-through during horizontal scroll. Tests: tests/unit/components/create-smart-table.test.ts.
  • Bulk field-edit UI modal — created bulk-field-edit-modal.svelte with Svelte 5 runes, strict FieldInstance schema typing (zero any), full WCAG 2.2 AA accessibility, and automated widget input mapping (text, number, boolean, date). Integrated with Alt+E shortcut in entry-list-multi-button.svelte, entry-list.svelte, and batch API handler in entry-actions.ts. Tests: tests/unit/utils/entry-actions.test.ts, tests/unit/utils/api.test.ts.
  • Vitest dynamic multi-core worker scaling — scaled worker pool dynamically based on host CPU core count (cpuCount >= 16 ? Math.min(16, cpuCount - 4) : cpuCount - 1) with process.env.VITEST_MAX_WORKERS override in vitest.config.ts, utilizing up to 16 concurrent workers on 24-thread hosts while leaving 4 cores for OS and IDE responsiveness.
  • Individual test bottleneck elimination — severed dynamic import("@src/hooks.server") in user/auth mutations by moving invalidateTurboAuthForUser into lightweight handle-turbo-get.ts (auth-lockout.test.ts dropped from 2,270 ms to 81 ms, user-attribute-update.test.ts from 1,439 ms to 75 ms); replaced dynamic resetModules() with static imports in testing-api-gate.test.ts (466 ms → 75 ms); added retryDelayMultiplier: 0 in seed-burst.ts (eliminating 200 ms wall-clock sleep; 312 ms → 86 ms); moved unmocked dynamic DB import to beforeAll in database-resilience.test.ts; added cmd.flushDb() on test initialization in cache-service-l2-contract.test.ts for clean Docker Redis isolation.
  • Suite acceleration metrics — unit suite accelerated from 48.27 s → 18.79 s (~61% speedup, 2.57× faster) across 515 files / 4509 tests with 100% pass rate and isolation preserved (isolate: true). bun run check clean, risk:audit 5/5, lint:tenant -- --full clean.

Authorization (dispatcher + handler) — POST /api/user (bare namespace root) was exempted from the endpoint gate because the dispatcher treated “no action segment” as public, so any authenticated user could create accounts carrying privileged fields; GET /api/user (directory listing) was exposed the same way. Roots now fall through to user:write/user:read, handleCreateUser strips privileged fields for non-admins via sanitizeClientUserAttributePatch, and "roles" joined PRIVILEGE_ESCALATION_FIELDS. Behaviour change: GET /api/user now requires user:read.

Token namespace — /api/token/* was a public-route deny-list, which also kept the dispatcher’s api:token mapping out of the picture for PUT/DELETE. It is now a positive allowlist: only GET /api/token/validate-token/{value} is public; list/create/resolve/batch and PUT/DELETE require an admin session or the mapped api:token permission, and updates are field-allowlisted (expires, blocked, consumed; admins additionally email, role; never user_id, type, token, _id, tenantId). Behaviour changes: non-admin sessions can no longer create/list/update/delete tokens; unauthenticated GET /api/token/{value} now 401s; the public validation contract is GET /api/token/validate-token/{value}.

GraphQL APQ — extensions.persistedQuery keys must now be exactly 64 hex characters and sha256(query) is compared constant-time before a query may be registered, closing cache poisoning/flooding of the persisted-query store (PERSISTED_QUERY_HASH_INVALID / PERSISTED_QUERY_HASH_MISMATCH). Registered queries keep the L1 RAM / L2 Redis store with a 7-day TTL.

Request-size & page-size ceilings — chunked bodies (Transfer-Encoding: chunked, no Content-Length) are counted through a zero-copy stream in the dispatcher and bounded at the same 15 MB API_MAX_BODY_SIZE_BYTES ceiling as declared lengths (413 PAYLOAD_TOO_LARGE); the streaming media route receives that counted stream undrained so incremental multipart parsing still works, and parseMultipartStream is called with explicit caps and aborts incrementally instead of buffering toward its 1 GiB / 5 GiB defaults. Pagination limit is clamped centrally at MAX_PAGE_SIZE = 200 in the REST parsers, the SDK find funnel and the GraphQL fast path + resolver.

Guardrail suite — tests/unit/api/authz-bypass-guard.test.ts (27 tests) enumerates every /api/<namespace> root and every public-route exemption against a reasoned contract, so a new authorization bypass fails a unit test rather than shipping.

Performance — zstd is no longer synchronous on the event loop (async native API; size-gated sync fallback at SYNC_MAX_SIZE 64 KiB); count-cache entries carry both logical and physical collection spellings and the write path clears both (list totals were stale for the 30 s TTL); content.nodes.bulkUpdate collapses N per-row DELETEs into one IN (...) and reorderStructure batches per-item SELECT * into one projected IN (...) read (2N → N+1 on SQL); the security gate no longer computes and discards duplicate surface/UA scans (≈4 string scans + 2 allocations per API read → 2 + 1); the tenant proxy memoizes per-property wrappers; auth row conversion uses the registered-schema fast path on PostgreSQL/MariaDB too; media gallery composite indexes (tenantId, folderId, updatedAt) + (tenantId, updatedAt) were added to all three SQL dialects and the system schema spec, with idempotent boot tails for existing installs; commerce order downloads do one batched product lookup instead of one per line, and internal analytics/redirects callers no longer depend on the capped public page size.

Two pre-existing product bugs found and fixed by this work (not known before) — plus a MariaDB repair gap:

  • Numeric sort directions were mis-mapped to DESC in every SQL sort emitter — {status: 1} emitted status DESC while the keyset cursor said ASC, so keyset pagination repeated rows and terminated early on SQLite/MariaDB/PostgreSQL. A single normalizeSortDirection() in core/page-utils.ts now feeds page-utils, drizzle-sql-helpers, sql-adapter-core and the query builder.
  • MongoDB reorderStructure never worked — it called mongoose.startSession() on the global default connection while the adapter owns a mongoose.createConnection, so every drag-reorder returned success: false. It now uses a single ordered bulkWrite, matching the adapter’s declared supportsTransactions: false.
  • MariaDB repair gap — ADD COLUMN IF NOT EXISTS could never fix an existing mfaVerifiedAt datetime (fsp 0) column, so millisecond MFA timestamps silently truncated; a guarded boot tail now runs MODIFY COLUMN … DATETIME(3) once per install.

Tests: tests/unit/api/authz-bypass-guard.test.ts (27), tests/unit/api/dispatcher-body-limit.test.ts, tests/unit/media/streaming-upload.test.ts, tests/unit/databases/media-gallery-indexes.test.ts, tests/unit/databases/content-node-batching.test.ts, tests/unit/databases/mongo-reorder-structure.test.ts, tests/unit/core/auth-row-converter-parity.test.ts; contract suites extended in find-page-count, cache, content-nodes, auth-session-amr (tests/integration/databases/) and tests/integration/api/token.test.ts.

3.19 September 2026 — Version tooling: derived bumps, real SemVer, one update check

Version source — bun run version:bump [auto|patch|minor|major|<x.y.z>] (scripts/version.ts) derives the next version from the highest v* tag reachable from HEAD (note: git describe returns the nearest tag here, which disagrees, so the script prints the discrepancy). auto implements SemVer from Conventional Commits: feat → minor (§7), breaking markers → minor while major is 0 (§4, matching release-please’s 0.x rule) / major afterwards (§8), everything else → patch (§6), with breaking > feat > rest when a range mixes types. An explicit <x.y.z> target sets the manifest exactly, for releases that must skip or pin a version. Guards: refuses a dirty manifest, refuses to re-release an already-tagged version (the failure the workflow previously swallowed as “Tag already exists”), refuses a downgrade, and never commits or tags. The policy is a pure exported decideBump() with 33 unit tests, mutation-checked.

Mode parity / ledger — the correction from the first pass: an apparent cache-hit-ratio regression (+83 %) was a baseline mode mismatch (a TEST_MODE=true standalone baseline compared against production-parity matrix runs; the same build re-measured in the baseline’s own mode is 0.538 ms). Trend rows now carry a server-mode stamp, matrix recording refuses non-production servers, and contaminated ledger rows were purged.

App side — two parallel version services were collapsed into services/core/version-service.ts (readLocalVersion, checkForUpdates, compareVersions implementing SemVer §11 prerelease precedence: 0.0.10-dev.3 orders after 0.0.9 and before 0.0.10), the observability duplicate deleted, and the fake unit test (it re-implemented its subject and asserted its own copy) replaced with 21 tests against the real implementation. The badge was simplified from ~10 parallel state variables to one state object with derived presentation — which fixed a real defect: the loading guard made the initial call return immediately, so the update indicator had never fetched at all. ai-service.ts no longer hardcodes a version in its outbound User-Agent/payload.

Coverage added — the previously unmeasured claims are now measured: media-gallery composite indexes (planner-proven index_used=1; 0.396–0.410 ms on 4 000 rows SQLite, 1.248 ms PostgreSQL), zstd-async (477 KiB GET 6.0–6.2 ms avg, 300–315 RPS, co-tenant p95 0.37 → 1.06–1.48 ms — no event-loop stall). The body-limiter and scan-dedup changes remain deliberately unmeasured (sub-µs work on a ≥1 ms path, ~3 orders below the noise floor).

3.20 September 2026 — Unified Translation Cockpit & Multilingual Studio Inspector

A comprehensive multilingual management and inspection suite bridging admin UI chrome localization with per-field content readiness:

  • Dual-Lens Language Management: Simultaneously tracks and toggles UI system language (Paraglide compile-time catalogs) alongside content editing languages (systemLanguage vs contentLanguage) within translation-cockpit.svelte.
  • Document-Level Translation Completeness: Computes real-time translation completion percentage per document across all configured system languages via computeDocumentTranslationSummary() (src/utils/translation-analytics.ts).
  • Field-by-Field Deficiency Navigation: Visual deficiency matrix identifies untranslated fields per language. Clicking any deficiency jumps directly to the field, switches the content locale, scrolls the field into view, and triggers an attention-highlight pulse.
  • 1-Click AI Translation: Integrated 1-click batch translation of individual missing languages or entire documents via /api/ai/translate, preserving field structures and formatting without manual copy-pasting.
  • Design Token & Accessibility Compliance: Status-shade tokens (success, tertiary, warning, error), WCAG 2.2 AA compliant modal with focus trap and restoration. Tests: tests/unit/utils/translation-analytics.test.ts.

3.21 September 2026 — Dependency & Runtime Lean-Out

Execution of the full Lean-Out initiative to shrink bundle size, drop unused dependencies, and minimize runtime surface without feature loss:

  • Complete Lodash Elimination: Fully purged lodash and lodash.memoize across the codebase; replaced with native ECMAScript methods, typed object helpers, and fast-deep-equal.
  • Modularized On-Demand AWS SDK: Removed monolithic AWS SDK clients in favor of isolated @aws-sdk/client-s3, loaded dynamically on-demand in src/utils/media/storage-adapters.ts so storage drivers incur zero boot-time or bundle penalty when unused.
  • Dynamic MapLibre Loading: Geocoding and map rendering in src/widgets/custom/address/input.svelte moved to lazy dynamic import (await import('maplibre-gl')), keeping the primary widget bundle lean.
  • Zero-Shiki Runtime Surface: Removed unused shiki from dependencies and generated SBOM, eliminating redundant node dependencies.
  • Tiptap Lazy Evaluation: Rich text editor core and extensions are dynamically loaded (import('./tiptap')) on component mount in src/widgets/core/rich-text/input.svelte, keeping edit views lightweight on initial render.
  • Store Architecture Consolidation: Consolidated global stores from 28 files down to 18 domain singletons, standardizing on Svelte 5 fine-grained reactivity ($state(), $derived()) across all 72 widget inputs, displays, and app shell routes.

3.22 September 2026 — Smart Table Advanced UX & Adaptive Grid Platform

A comprehensive overhaul of the core Smart Table data orchestration engine (src/components/ui/smart-table/ and src/components/collection-display/), delivering enterprise-tier UX, multi-column sorting, compound query operators, and accessibility compliance:

  • Multi-Column Sort: Compose secondary and tertiary sorts via Shift + Click or headless setSort(field, { multi: true }). Renders visual ordinal rank badges (1, 2, 3) alongside directional indicators, cascades multi-criteria sorting in client-side comparators, and serializes compound queries to server URL parameters (sort=field1:asc,field2:desc).
  • Advanced Filter Operators UI: Integrated AdvancedFilterModal compound filter builder supporting multi-clause AND / OR expressions across collection schema fields with contextual operators (equals, contains, starts_with, gt, gte, lt, lte, is_empty, is_not_empty). Active clause counts surface in the filter bar.
  • Export Filtered Selection (Alt + X): Browser-streamed CSV download bounded specifically to currently selected rows or the active filtered result set, accessible from both keyboard shortcuts (Alt + X) and EntryListMultiButton.
  • Keyboard Grid Navigation (WCAG 2.2 AA / ATAG 2.0): Fully keyboard-navigable grid using roving tabindex. Arrow key cell traversal, Home/End jump navigation, PageUp/PageDown leaping, Space for row selection, and Enter for direct entry editing.
  • Responsive Card Mode: Dynamic view mode switcher (Table View vs Card View), persisted per-collection via SmartTableLayoutPrefs, with responsive fallback on mobile viewports (<768px).
  • Unit Testing: 7 new test cases in tests/unit/components/smart-table-advanced-ux.test.ts covering multi-sort composition, cyclic direction toggle, rank indexing, client-side tie-breaking, and view mode persistence.

3.23 September 2026 — Surgical turbo invalidation (soak / mixed-write)

A single-entry PATCH used to wipe every turbo findById for that collection (invalidateLocal string-includes + res:<collection> tags on point-reads). L2 already cleared doc:<coll>:<id> surgically; L1 did not. Point-reads now tag only doc:, skip the shared L1, and invalidateLocal({ entryIds }) drops lists/GraphQL plus the written ids. In-repo replica (SQLite, 10k docs, 8c, 2026-09-20): mixed 1,447 RPS, 60s soak 1,362 avg RPS. Tests: tests/unit/core/request-classifier-and-response-cache.test.ts, tests/unit/sdk/collections-post-write.test.ts. Docs: Competitive Comparison, Performance Architecture.

3.24 September 2026 — Bounded caches, exact L1 accounting, batch seed lane

Three separate findings from a soak/seeding/CPU pass, each with its own measurement (4f3216920 + this change, SQLite, production-parity standalone benchmarks, shared 24-core host):

  • The media storage existence cache is bounded now (was an unbounded Map). src/utils/media/media-storage.server.ts cached every storage exists() probe — positive and negative — in a Map with a 10 s TTL and no cap and no eviction order, so its size grew with the number of distinct media paths a process ever probed. It is now an LRUCache with SVELTY_FILE_EXISTS_CACHE_MAX (5,000 entries, LRU-ordered) and SVELTY_FILE_EXISTS_CACHE_TTL_MS (10,000 ms) — TTL and refresh: true semantics are unchanged. Test: tests/unit/media/file-exists-cache.test.ts (400 distinct paths flood, a 50-entry cap holds, negatives and positives both cached, TTL expiry, refresh bypass). The soak rows cannot show this one: the soak’s mix (health/list/item/schema/create) never probes a storage path.
  • The response-cache L1 byte budgets did not bind — the counters drifted on every path. src/services/cache/response-cache.ts maintains l1Bytes/pointL1Bytes per tier; the L2-refill paths (get()/getAsync()) inserted without accounting and invalidate/invalidateAll/doc-scoped drops/TTL expiry deleted without decrementing. Measured on the pre-fix code with the budgets pinned to 1 MB: 40 refilled entries 960,000 bytes resident while the counter read 0, and after 10 sets + 1 key invalidate + 1 doc-drop + 1 expiry the list counter read 176,000 against 144,000 real bytes (point tier 160,000 vs 128,000) — i.e. the 64 MB/16 MB budgets (SVELTY_L1_MAX_MB, SVELTY_L1_POINT_MAX_MB) were only ever consulted against a number unrelated to the store, and the 2,000-entry cap was the only real bound (≈200 MB of 100 KB list bodies). Every mutation now runs through one putEntry/removeEntry primitive, enforcement runs after the insert (strict size ≤ cap and bytes ≤ budget, an oversized single entry is dropped rather than pinned), the budgets re-read their env value on change, and responseCache.getL1ByteStats() exposes the counters plus an exact recount for diagnostics. Tests: tests/unit/core/response-cache-l1-budget.test.ts — 5 tests, all 5 failing on the pre-fix implementation and passing after (insert-above-budget shrinks both tiers, refill accounting, delete-path accounting, counter == recount through a mixed torture sequence). Docs: Cache System.
  • Seeding a 100k dataset got 5.3× faster through the documented bulk endpoint. seedHttpCollectionBurst posted one HTTP create per row; it now claims chunks off a shared cursor and posts POST /api/collections/:id/bulk (500 items, the registered migration-scale batch size), retries transient statuses per chunk, and falls back to per-item creates for a range whose chunk fails or returns a mismatched row count — the dataset-integrity gate (created === requested) is untouched. Same build, same fresh DB, 100k docs, 8 workers: 74.77 s / 1,337 docs/s → 14.20 s / 7,042 docs/s; at 20k docs 18.86 s / 1,060 → 3.44 s / 5,815. Rows are equivalent (95,000 distinct slugs over 95,000 seeded rows, status/slug/tenantId/createdBy/createdAt all present) and the file’s measured rows still exercise the per-item lane (create/update/mixed unchanged; chunkSize: 1 reproduces the old path for A/B). The seed phase is now a registered trend row — Seed Burst (<N> docs, HTTP) plus competitive.seed.docs_per_s / competitive.seed.wall_ms — instead of being invisible between two phases. Tests: tests/unit/bench/seed-burst.test.ts (batch lane + order, chunk retry, fallback, per-item lane, integrity gate).
  • The per-item write floor, measured. With PROFILE_WRITE=1 on the HEAD build, a create’s server-side cost splits ns:persist 2.06 ms (of which ns:persist:adapter-write 1.77 ms, db:ins:stmt 0.97 ms) against ns:beforeSave 0.07 ms, ns:encrypt 0.03 ms and ns:prep:* 0.01 ms; hooks-performance.test.ts puts the mutation lane’s middleware cost at 1.633 ms warm (audit disabled) over a 0.35–0.65 ms read lane. That is the shape of the create row: a single-statement INSERT under the SQLite write mutex plus a single-threaded JS pipeline — no per-item step I could find is removable without changing write semantics (per-hook attribution stays unavailable, see roadmap).
  • The soak’s RSS growth is stepwise, not monotonic — and it is not the two SQLite knobs. Bounded 3-minute variants (LONG_SOAK_HOURS=0.05, fresh DB per run, first/last bucket reported): +54.4 MB on the pre-change build, +52.8 MB with SQLITE_MMAP_SIZE=0 SQLITE_CACHE_SIZE=8000 (both native ceilings minimised — refuting the mmap/page-cache hypothesis that was the cheapest explanation), +53 MB on the new build. A 6-minute variant shows the shape: RSS steps +54 MB and +105 MB at ~2 and ~4 minutes and is otherwise flat (+0.6–3.6 MB per 30 s bucket between steps), while heapUsed oscillates 73–168 MB without a trend — i.e. V8 committing heap pages, not an object graph that grows with request count. A 3–6 minute window cannot separate commitment from a slow leak; that needs the long row in roadmap-2026.

3.25 September 2026 — Long-horizon soak: the memory verdict, measured against controls

The bounded soak could not separate V8 heap-commitment steps from a slow leak (see 3.24). The long row now exists, with the two controls the verdict needed — and the answer is that nothing leaks; what grows is commitment, and it converges.

Run matrix (one Windows 11 host, Intel Core Ultra 9 275HX, 24 threads, 63.4 GB RAM; node index.cjs adapter-node server, Node v24.15.0, SQLite node:sqlite, --max-semi-space-size=128 --max-old-space-size=1024; 4 workers, ~97 req/s, 0 errors in every run; slopes are the harness’s own steady-window/tail fits in MB/min with t = slope/se):

Profile / build Window Writes RSS start → end RSS tail (t) heapUsed tail (t) heapTotal tail (t) Latency drift Verdict
mixed 1 h (HEAD) 54.5 min steady 34,878 218.5 → 430.3 MB +1.35 (6.5) −0.10 (−0.2) +0.15 (9.6) −0.000 ms/min STABLE (No Leak)
read-only 45 min (HEAD) 39.5 min steady 0 221.9 → 347.2 MB −2.47 (−12.1) −0.24 (−0.3) −0.01 (−1.7) −0.001 ms/min STABLE (Plateau)
idle 45 min (HEAD) 39.5 min steady 0 221.9 → 77.6 MB −5.40 (−14.6) +0.05 (8.2) +0.08 (11.1) 0.000 ms/min STABLE (No Leak)
mixed 45 min, page cache 8 MB 39.5 min steady 26,070 221.4 → 370.5 MB −2.13 (−13.9) +0.02 (0.0) +0.27 (19.7) +0.002 ms/min STABLE (No Leak)
mixed 45 min, page cache 256 MB 40 min steady (2026-09-22) 26,040 218.4 → 476.3 MB +0.28 (2.0) +0.06 (0.1) +0.30 (20.8) — STABLE (Plateau)
  • The growth is V8 commitment, not retention. Every loaded profile shows the same shape: heapUsed oscillates 72–176 MB with a tail slope of ±0.24 MB/min (|t| ≤ 0.3), external is flat, and heapTotal steps up to an operating point that three independent runs land on at 342.8 / 344.0 / 347.9 MB — that is the documented --max-semi-space-size=128 young generation (2 × 128 MB) plus old space, ~3.5× the post-GC live set. idle releases the whole commitment (RSS 221.9 → 77.6 MB), which is what a ceiling that is never needed looks like.
  • The earlier “off-heap” reading was a working-set effect, not native growth. An independent 15 s Windows sampler over 32.5 min of the mixed run: working set −0.60 MB/min (t = −5.8) (late window −1.34, t = −9.6) while privateBytes grew +0.49 MB/min (t = 13.2) and the handle count stayed flat (+0.004/min, t = 1.1, 266 handles). Commit charge rising while residency falls is V8 asking for pages, not a native allocator holding them — and the flat handle count rules out the file-descriptor/timer leak class outright.

3.26 September 2026 — Field-level i18n toggle & GraphQL CSRF parity

Two rows closed from the competitive feature-gap list, both self-described with regression tests:

  • Field-level i18n UI toggle. The runtime was already complete (FieldInstance.translated, per-language value maps, contentLanguage plumbing, translation cockpit, per-field locale badges) — the missing piece was configuration UI. The Collection Builder field dialog now has a Translated checkbox beside Required (modal-widget-form.svelte), and the GUI → code generator emits translated: true so the flag round-trips for code-defined collections (collection-code-generator.ts; regression guard in tests/unit/collectionbuilder/code-generator.test.ts). Docs: Language & i18n Architecture.

  • GraphQL CSRF gate. The REST dispatcher was CSRF-gated; POST /api/graphql was not. The route now enforces the same gate on every authenticated POST — same-origin fast path or double-submit token (constant-time, rotated) — because Yoga batching (limit 10) can hide mutations inside a POST; GET stays exempt (safe method), API-key/token callers are exempt (no cookie to ride), TEST_MODE skips exactly as on REST. Coverage: tests/unit/api/graphql-security.test.ts (cross-origin rejection 403, same-origin fast path, token path, API-key exemption). Docs: GraphQL API.

  • Deep-offset cliff closed (W2): REST keyset cursor. The adapter-level composite keyset was contract-tested; the missing piece was the REST surface. GET /api/collections/{id}?cursor=… now parses the opaque cursor, the SDK find keyset mode applies the _id tiebreaker to the ORDER BY, fetches limit+1 and emits meta.hasMore / meta.nextCursor; cursor pages bypass the query cache by design so meta is never dropped. Contract: tests/integration/api/collections-keyset-cursor.test.ts (asc + desc walks over duplicate sort values visit every row exactly once). Docs: Collections API, Scale limits.

  • Client-bundle Node leak fixed (email preview page). +page.svelte on /email-previews dynamically imported the @better-svelte-email/preview package ROOT, dragging fs/url and Svelte’s server render-context.js (node:async_hooks) into the client bundle — the build emitted externalization warnings and the previewer could never run in a browser anyway. The page now imports the package’s client-safe ./EmailPreview.svelte subpath export and passes the component’s real page prop shape; the build is clean (zero externalized for browser warnings).

  • The SQLite page cache is properly refuted now (the 3–6 min test could not do it). Same-size dataset (439,466-row collection_BenchmarkStable, 643 MB copy), same load, only SQLITE_CACHE_SIZE changed: 8 MB (26,070 writes) vs 256 MB (26,040 writes) gives heapTotal tail +0.27 (t = 19.7) vs +0.30 (t = 20.8) and a declining RSS tail in both. Neither the mmap ceiling nor the page-cache budget is a memory lever at this load, so the hardware-profile defaults stay as documented.

  • Latency stayed flat at constant throughput in all three loaded rows (drift |≤ 0.002| ms/min, p95 2.5–4.3 ms, reqs/s 96–98 with 0 errors) — no cache-growth or lock-contention degradation to attribute.

Two instrument gaps closed (the reason the earlier pass could not answer this):

  1. GET /api/system/health?verbose=true omitted arrayBuffers from its memory object, so every soak series recorded a structural 0.00 for it and the buffer-growth half of the off-heap branch was blind. It is now reported (src/hooks.server.ts), which also satisfies the long-row requirement in roadmap-2026.
  2. The soak now trends the native/other residual (rss − heapTotal − external) as its own series, summary row (Native/Other Tail Growth (t)) and metrics (soak.resid_slope_mb_min, soak.resid_tail_slope_mb_min) — the number this investigation had to derive by hand to find the owner of an off-heap slope. Contract: benchmark-matrix. A latent ordering bug was fixed in the same pass: writeSeries() read writeReqs before its let was initialised, and the surrounding catch swallowed the temporal-dead-zone error — the first series flush was silently dropped.

Methodology: self-measured with the registered harness (LONG_SOAK_PROFILE axis + LONG_SOAK_HOURS=1), production-parity server mode, one host, SQLite only. The 45-min controls and the page-cache A/B overlapped the 1 h run on the same host (the idle/read-only servers carry ~0 load); the 1 h rows are the ones to compare against a clean single-run host. Commands, series JSON and the raw per-bucket curves are in tests/benchmarks/results/<adapter>/soak-series-*.json.

3.27 October 2026 — L1 cache moved to the native dependency-free FastLRU

The hybrid cache’s L1 in-memory tier (cacheService.l1) moved off the external lru-cache package onto the repo-native FastLRU (src/utils/native-utils.ts), so the L1 is now fully dependency-free; lru-cache was removed from package.json (direct dependencies 88 → 87, SBOM components 683 → 682). FastLRU was extended to cover the full L1 surface: byte-budget eviction (maxSize + maxEntrySize + sizeCalculation), per-entry TTL, dispose, and the read hints updateAgeOnGet / updateRecencyOnGet.

Self-measured microbenchmark (throwaway script, 2026-10-06, Bun 1.4.2, N = 100,000 ops after ≥1,000 warm-up iterations; sizeCalculation with a 128 MB maxSize and 16 MB maxEntrySize, 20k keys, 120-byte string values):

  • get() with recency promotion: 31.3 M ops/s (lru-cache) vs 13.6 M ops/s (FastLRU) — a ~36 ns/op absolute difference, negligible against a ~200 µs request.
  • get() peek (updateAgeOnGet: false, updateRecencyOnGet: false): 28.7 M vs 19.4 M ops/s.
  • set(): 3.67 M vs 4.51 M ops/s (FastLRU ~1.23× faster).
  • 2,000-entry count-only cache get(): 12.4 M vs 6.87 M ops/s.

This supersedes the earlier “5.46 M vs 780 k (7×)” figure, which compared a Map LRU rather than the L1’s sizeCalculation path. The ENTRY read path now genuinely “peeks” — it skips recency promotion and TTL refresh (updateRecencyOnGet: false), the intent a comment previously stated but lru-cache, which always promoted on get, did not realize; CONTENT reads still promote and refresh. Docs: Cache System, Production Deployment.

3.28 October 2026 — Dead-code sweep & slop-scanner signal/noise split

The slop scanner’s unused-export report was split into two clearly-labelled buckets so the actionable set is never drowned by the public/test API inventory: dead-code candidates (unused-export — a symbol referenced nowhere in src/scripts/root entries/tests/docs) and export-surface (referenced by tests/docs only — test-only reset helpers and documented public API). The scanUnusedExports reference set now also reads the repo-root entries (*.{js,mjs,cjs,ts}, e.g. index.server.mjs, index.cjs) and scripts/** matches .mjs/.cjs, so bundled-startup exports are no longer misreported as dead. All 20 live dead-code candidates were removed across 15 files (verified definition-only repo-wide), and the export-surface inventory is collapsed by default — list it with --surface / --verbose.

Result: bun run slop is 0 errors / 0 warnings / 0 dead-code candidates, with the 137 documented/test-only exports tracked as annotated inventory. Docs: Git Workflow, Testing Scripts Catalog.

4. Final Sprint Achievements (March 2026)

Having secured the “Engine War” with dated, self-measured performance and security ledgers rather than absolute performance claims, SveltyCMS has now closed the Editorial Depth gap.

4.1 Visual In-Context Editing

  • Features: Drag-to-reorder blocks in preview, field-level pulsing highlights, and auto-scroll-to-field interactions via the Enterprise Handshake Bridge. July 2026: Website Starter integrates Svedit inline editing on the public site; license gate on POST /api/preview/authorize enforces paid bridge while CMS form saves remain free.

  • Freemium model: Public site rendering and CMS form editing (including Svedit JSON in pages.content) require no plugin license. Live Preview tab, iframe bidirectional sync, and inline Svedit click-to-edit require Editable Website (€14.99 marketplace, 14-day trial auto-started on Website Starter setup).

  • Performance Optimization (March 2026): Implemented Deferred Rendering for the preview iframe. The handshake and iframe mount are lazy-loaded only when the user first switches to the Live Preview tab, reducing initial page load CPU/Network by 60% while maintaining instant switching for subsequent use.

  • Fail-Closed API Security (April 2026): Implemented a central dispatcher gatekeeper requiring mandatory endpoint registration. Any unmapped or unauthorized request is denied by default, eliminating “Shadow API” data leaks.

  • Widget Performance Auditing (April 2026): Integrated an automated overhead pass into the Enterprise Matrix to measure the server-side “cost” of every core widget, ensuring “Zero-Overhead” standards are maintained as the ecosystem grows.

  • Result: An editorial experience we assess as competitive with legacy SaaS platforms (self-assessment; no third-party UX benchmark).

4.2 AI-as-a-Field-Type & Hosted Knowledge

  • Features: Native AIEnrichment field type, smart context-aware translation, and the Hosted MCP Context Server (mcp.sveltycms.com) for real-time agentic memory.
  • Differentiator: Local inference via Ollama ensures data privacy while maintaining high-speed content enrichment.

4.3 Visual Workflow Engine (FSM)

  • Features: Node-based Finite State Machine editor for complex, non-linear content lifecycles. Includes transition guards, automated side effects, and HMAC-signed approval links for external stakeholders.
  • Result: Provides enterprise-grade governance comparable to high-end SaaS DXP platforms.

4.4 Advanced Media Engine: Transcoding & Batch Hub

  • Features: Multi-resolution HLS/MP4 adaptive bitrate pipeline and bulk Sharp.js filter processing (Vivid, B&W, HDR) for 100+ assets simultaneously.
  • Result: Eliminates the need for external Digital Asset Management (DAM) or video processing services.

4.5 Visual Logic Builder

  • Features: Recursive JSON-based conditional engine supporting nested AND/OR groups for complex showIf and requiredIf dependencies.

4.6 Real-Time Collaboration & Concurrency

  • Features: Conflict-free Replicated Data Type (CRDT) concurrent editing powered by Yjs, transported over adapter-node-compatible channels — SSE (/api/events + /api/collaboration/yjs) for the default path, plus a native ws-based sync server on /ws (yjs-sync-server, wired in the production index.cjs entry). No uWebSockets / svelte-realtime dependency.
  • Server state: yjs-service (src/services/collaboration/yjs-service.ts) holds per-docId Yjs documents, keyed by tenant, applied from EventBus updates; GET /api/collaboration/yjs returns full state for client bootstrap. September 2026: docId must be entry:{collectionId}:{entryId}; handlers check collection-scoped collection:read / collection:write (then global), admin fast-path.
  • Client integration: collaboration-service.svelte.ts + SseProvider (SSE transport), wired into the field editor (fields.svelte) when a collection enables collaboration.enabled. Awareness protocol carries cursor/presence per editor.
  • Latency: self-measured event-push latency is in the low-ms class on a local deployment; no sub-ms claim is made (network RTT dominates outside localhost).
  • Result: Production-oriented concurrent editing for collections that opt in; it is not a claim of Google Docs parity or of GraphQL-over-WebSocket subscriptions (not shipped).

4.7 CLI Maintenance Suite & Global Accessibility

  • Features:
    • CLI Cache Control: Native bun run cache:clear utility supporting system-wide, all-tenant, or specific tenant invalidation.
    • Performance Trends: Automated trend analysis (🟢/🔴) in the Enterprise Benchmark Matrix, tracking regression across REST, GraphQL, and Widget layers.
    • Intelligent RTL support: Automatic Right-to-Left detection and alignment in the RichText editor for Middle Eastern locales (AR, HE, FA).
  • Result: Reduces operational overhead for DevOps and ensures a first-class editorial experience for a global workforce.

4.8 Q2 2026 Competitive Response Sprint

In direct response to rapid iterations from Sveltia CMS and Directus 11, SveltyCMS has prioritized the following “Parity & Leapfrog” items for April 2026:

  • Sveltia Parity Suite: Implemented locale-agnostic slug preservation, improved regex normalization for international characters, and CI-linked “Publishing…” status indicators in the editor shell.
  • Smart Diff UI: A dedicated comparison modal within the Enterprise Monitor that filters for “only modified fields,” allowing editors to review changes in 100+ field collections in seconds.
  • Hardened Auth Security: Field-level access control (FLAC) applied to internal authentication fields (email, provider, role), matching recent security hardening in Payload 3.81.
  • One-Click Sveltia Importer: A native migration utility that parses Sveltia config.yml files and Git-based content to instantly scaffold SveltyCMS collections and import markdown data.
  • Managed Cloud Tier: Collaborative infrastructure offering managed Postgres/Redis via Fly.io, targeting SaaS users who require SveltyCMS performance without operational overhead.

May 2026 Additions — Security & API Hardening:

  • 4-Layer Defense-in-Depth Authorization: Zero-trust re-validation at Middleware, Dispatcher, Handler, and Page Action layers. Cookie prefix hardening (RFC 6265bis), setup completion gating, admin verification, media permission checks, centralized permission guards.
  • Timing-Safe Cryptography: crypto.timingSafeEqual on all security-sensitive comparisons (test secrets, TOTP codes).
  • ETag Conditional Requests: SHA-256 ETag headers with 304 Not Modified support for bandwidth-efficient client caching.
  • API Versioning: X-API-Version: 1 header and /api/v1/ path prefix routing for future-proof API evolution.
  • 56 New Security Tests: Defense-in-depth unit tests (51) + session fixation prevention tests (5). Total test suite: 861 tests, 105 files, 0 regressions.

5. Phase 3: The Enterprise Leap (Q2-Q3 2026) ✅ COMPLETE

To cement its position as the undisputed category leader against platforms like Payload CMS, SveltyCMS is deploying three critical architectural features that transform it from a high-performance engine into a fully-fledged enterprise application platform.

5.1 Database-Backed Background Jobs Queue

  • The Gap: Edge and serverless functions (like Vercel) time out during heavy operations (e.g., Sharp/WebP generation, AI enrichment), leading to brittle media workflows.
  • The Solution: A native svelty_jobs table managed by the database-agnostic JobQueueService.
  • Advanced Features (March 2026):
    • Concurrency Guard: Implemented IMPORT_CONCURRENT_MAX to prevent server exhaustion during simultaneous multi-tenant bulk operations.
    • Real-Time Progress Tracking: Jobs now support granular progress updates (e.g., “450/1000 items processed”), allowing for a more transparent editorial experience.
    • TTL-Based Temporary Store: Large job payloads are offloaded to a temporary file-backed store with automatic TTL cleanup, preventing DB index bloat.
  • Impact: Heavy workloads are offloaded to a persistent polling worker (or CRON fallback), eliminating front-end blocking and save-failures entirely. This is the cornerstone of enterprise stability.

✅ Completed June 2026: Adaptive job scheduler with exponential retries, API management, and audit integration.

5.2 The Zero-Latency “Local SDK”

  • The Gap: Fetching content via HTTP within the same SvelteKit runtime adds unnecessary serialization overhead.
  • The Solution: Injected a formal, typed cms instance into event.locals via middleware.
  • Impact: Developers can query the database directly in +page.server.ts (await locals.cms.find('posts')) with zero HTTP latency, achieving true full-stack SvelteKit synergy.

5.3 Virtual “Join” Fields

  • The Gap: Traditional relational graphs cause either N+1 query performance deaths or massive over-fetching. Traditional databases also struggle with “inverse” relationships (e.g., finding all comments for a post) without redundant field storage.
  • The Solution: Payload-style virtual join fields utilizing dynamic Drizzle queries. We introduced the Smart Join field type, which dynamically queries inverse relationships during the populate phase.
  • Impact: Keeps the sub-millisecond auth and fast SSR intact even on deeply nested, complex content trees. Developers can now model Post -> Comments or Product -> Reviews relationships with zero database bloat and maximum query efficiency.

5.4 Durable Webhook Engine & DLQ

  • The Gap: Standard webhooks often fail silently or flood endpoints during high-traffic bursts. Retrying 4xx “Poison Pills” wastes server resources and clogs worker queues.
  • The Solution: A job-queue-driven delivery system with HTTP status awareness. Retries are reserved for 5xx/429/Network errors, while 4xx failures are instantly routed to a Dead-Letter Queue (DLQ) for admin review.
  • Impact: Improves delivery reliability (retry on 5xx/429/network; 4xx → DLQ). Not a mathematical 100% delivery guarantee under prolonged outages.

5.5 Native Soft Deletes (Trash System)

  • The Gap: Accidental deletions in enterprise CMS environments often lead to permanent data loss or manual database restoration. Standard soft-deletes often face “Unique Index Collisions” when trying to re-use slugs of deleted items.
  • The Solution: A native trash system with “Mangle-on-Delete” logic. Unique fields are automatically suffixed with a deletion timestamp, and the safeQuery middleware ensures trashed items are globally isolated from production APIs by default.
  • Impact: Provides a safety net for content editors without the performance or indexing overhead of traditional archival strategies.

5.6 Per-Field Content Localization ✅

  • The Gap: Document-level i18n requires switching entire documents per language, making side-by-side editing impossible.
  • The Solution: Per-field Record<Locale, string> storage with inline locale switchers and AI-powered translation.
  • Impact: Editors can see all language variants of a field simultaneously, translate with one click, and track per-field translation progress.

6. Conclusion (truthful summary)

Is SveltyCMS “state of the art”? That is a product judgment, not a single score. Against our own design goals and self-measured suites:

Dimension Evidence (self-described / self-measured) Limits
Architecture Svelte 5 + shared SqlAdapterCore + 4 production DB adapters Still a full Node/Bun process; not magically edge-only for all features
Performance SQLite sub-ms CRUD; networked ~0.5–2 ms RTT; findPage / count cache product layer Not a global speed ranking; not same-harness vs Payload/Strapi/Directus
Security Fail-closed API, hashed tokens, RBAC, audit chain; scanners in CI 99.93/100 (~99.9%) internal checklist, re-checked 2026-09-29. Not a pentest.
Intelligence Behavioral learning, AI scaffolder, MCP, marketplace docs Learning warms caches; does not rewrite engines under load
Stability Soak / leak benches exist in-repo One 5-min soak is evidence of a path, not eternity

Verdict (neutral): SveltyCMS is a self-hosted headless CMS with strong documented security packaging in core, multi-DB adapters, and an automated benchmark culture. Use Competitive Comparison for buyer trade-offs (React ecosystem, marketplace size, MSSQL). Prefer reproduce (bun test tests/benchmarks/…) over absolute ranking language.

7. Milestone log (engineering, not trophies)

Selected 2025–2026 engineering milestones. Metrics are self-measured unless noted.

7.1 Core engine & DX

  • Toolchain: oxlint/oxfmt for fast lint/format (project-scale times vary by machine).

  • SQL hot path: prepared-statement / shared core work across SQLite, PostgreSQL, MariaDB.

  • Client shell lean-down (August 2026): eliminated the last two import * as m wildcard Paraglide imports (command-palette.svelte, modal.svelte.ts) that forced the full 968-message catalog into the eager (app) shell. Replaced with named imports + static key map; eager admin shell dropped 705 KB → 502 KB raw (−29%, gzip 188 KB) measured on the production build manifest. scripts/check-bundle-size.ts now detects the real admin-shell node (nodes/N.js) instead of only layout*.js files, so TipTap/email-preview leaks into the shell fail CI.

  • Memory: SDK allocation work targeting low growth under load (see soak benches).

  • Write-path index hygiene (September 2026): every content table was provisioned with two index twins that are strict prefixes of their keyset-pagination variants — (tenantId, status, updatedAt) beside (tenantId, status, updatedAt, _id) and (tenantId, updatedAt) beside (tenantId, updatedAt, _id) — so neither served a plan the tiebreaker index cannot serve, while every UPDATE maintained both. PostgreSQL, 2000 single-row updates per statement, interleaved: 31.6 µs → 13.1 µs per row (−58 %) for the whole SET/index path, of which the two twins alone are 8.4 µs (−27 %) and the data GIN 10.1 µs. The twins are now dropped explicitly (DROP INDEX IF EXISTS) in the PostgreSQL, MariaDB and SQLite createModel() — no legacy index is left behind — and fresh SQLite table definitions no longer declare them. The GIN stays: it earns its keep (dynamic-field containment filters 7.9–10.9 ms → 0.1–1.7 ms at 100 k rows) and remains opt-out per collection via jsonIndex: false. Stated honestly: at 8 concurrent workers the HTTP lane is I/O-commit-bound, so this statement-level reduction lands inside the ±5–10 % per-lane run-to-run scatter of a shared host (5-round medians; read lanes — which the change cannot touch — drifted 0.91–1.05× between the two variants); the statement-level figures are the reliable signal, and the payoff is WAL, disk and VACUUM rather than RPS.

  • Reusable CPU profiling for soak runs (September 2026): SVELTY_CPU_PROFILE=<dir> starts V8 sampling at boot and writes rotating .cpuprofile snapshots every 30 s (configurable, floor 1 s) with atomic .tmp + rename writes, so attribution survives a harness that SIGTERMs its child and SIGKILLs it — the exact case where node --cpu-prof, which writes only on a clean exit, produces no file. Off by default (one env read at boot, no imports); tests/unit/utils/cpu-profiler.test.ts pins the disabled path, snapshot loadability and the two-file rotation bound.

  • Middleware: hooks pipeline optimized; full-auth HTTP is ms-class, not free. August 2026 hardening sprint measured 0.583 ms avg / 0.768 ms p95 / ~1,442 RPS for the full security + auth pipeline (SQLite, self-measured 2026-08-10) vs the 0.856 ms July 31 baseline.

  • Widgets: ComponentLoader / tree-shakeable loaders.

  • DB parity: MongoDB, MariaDB, PostgreSQL, SQLite as production adapters.

  • Enterprise Security & Automated Response: Unified SecurityResponseService with Redis-backed state and a linear ReDoS-safe threat scanner (shared WAF + AuthGuard).

  • CI Pipeline Restoration: Playwright E2E suite stabilized across all supported databases.

  • Active Database Driver Validation: Eliminated “lazy connection” false positives in setup via mandatory SELECT 1 pings.

  • GraphQL JIT Execution Engine: Multi-fold increase in RPS via @envelop/graphql-jit.

  • API Benchmarking Suite: Native, reproducible suites (tests/benchmarks/rest-api-performance.test.ts, tests/benchmarks/graphql-api-performance.test.ts) that measure both surfaces under the same harness, so comparisons are like-for-like and date-stamped rather than asserted.

  • Advanced Security Isolation: Implementation of COOP/COEP headers and refined multi-tenant cookie security for cross-origin isolation.

  • High-Performance GraphQL: Resolver memoization and JIT execution reducing API latency and redundant database hits.

  • GraphQL.js 17 + Yoga (August 2026): Upgraded to graphql@17.0.2 with native graphql-yoga@5.22.0 support and zero-allocation AST visitors.

  • Tenant Management Admin Dashboard (July 2026): Full-featured tenant administration UI at /admin/tenants with create/suspend/activate operations, real-time quota visualization (users, storage, collections), and plan-level resource enforcement. Gated behind system-admin isAdmin && !tenantId guard. Backed by the database-agnostic ISystemAdapter.tenants interface with a MongoDB implementation via MongoCrudMethods and shared SQL implementation (RelationalSystemModule) across PostgreSQL, MariaDB, and SQLite. Integrated into the sidebar navigation for instant access.

  • CLI Maintenance Suite: Native cache:clear utilities for system-wide and tenant-specific operations.

  • 5-Pillar Content Architecture: Consolidation of 19 fragmented files into 5 high-performance pillars, reducing content system module overhead by 74%.

  • 99.9% Self-Healing Cache (April 2026): Implemented granular “Smart Repair” and Content Scanning (mtime-hashing + Worker Pool), reducing structural reconciliation time by 99.9% (210ms → 0.05ms).

  • Real-Time SSE Synchronization (March 2026): Replaced 10s client polling with Push-based Server-Sent Events, achieving sub-100ms synchronization across all admin instances.

  • Global Accessibility: Intelligent RTL (Right-to-Left) support in the RichText editor for Arabic, Hebrew, and Farsi.

  • Vectorized Request Modification: Refactored the core data transformation pipeline to support chunked batch processing, reducing function call overhead by 70% and ensuring the event loop remains responsive during 10,000+ item imports.

  • Atomic Content Versioning: Implemented ContentVersionManager utilizing native database atomic increments ($inc / RETURNING), eliminating race conditions in high-concurrency multi-tenant environments.

  • System-Level SDK Context: Enabled system: true execution in the Local SDK, allowing background services and migrations to bypass row-level permissions while maintaining high-performance direct access.

  • Multi-Tenant Slug Uniqueness: Dedicated Slug widget with tenantScopedUnique and disableUnique controls for granular identifier management.

7.2 Visual Intelligence & Enterprise Collaboration

  • Website Starter + Svedit (July 2026): Default Setup Wizard preset — in-repo SvelteKit site at /, Svedit block renderer, seeded homepage, headless toggle. Free site + form saves; paid Live Preview bridge.

  • SEO head rendering for the site starter (October 2026): the SEO widget’s stored per-locale data now lands in the public document head — <title> (SEO meta title first), meta description/keywords, canonical, robots, Open Graph, Twitter Card, JSON-LD and hreflang alternates — assembled by the pure builder in src/services/content/seo/seo-head.ts with the widget’s save-time entity escaping decoded exactly once and JSON-LD re-serialized break-out-proof (< → \u003c). ogImage/twitterImage media IDs resolve to absolute public URLs server-side; draft/preview renders always emit noindex. /sitemap.xml (paginated, so the SDK’s page clamp no longer truncates it) respects per-entry noindex and duplicate-canonical controls and maps pages to the real site routes with hreflang alternates; /robots.txt disallows the admin/auth/system routes. Pure helpers are unit-tested (tests/unit/services/seo-head.test.ts, sitemap-builder.test.ts).

  • SEO beyond the widget (October 2026): auto-generated site JSON-LD (WebSite on the homepage, BreadcrumbList from the URL hierarchy) rendered alongside the widget’s schemaMarkup; a dynamic /llms.txt for AI crawlers built from the published collections (sanitized, capped, cached per tenant); a Core Web Vitals readout in the widget’s Advanced tab that measures the canonical URL on demand through the PageSpeed Insights API (CrUX field data + Lighthouse lab data, threshold-color-coded, no key required — display-only by design); IndexNow on publish (premium: fire-and-forget submission to the Bing/Yandex/Seznam/Naver ecosystem, CSPRNG key auto-generated and served at /indexnow.txt, license-gated like the sitemap, skipped under skipSideEffects); and AI-crawler robots directives via the AI_CRAWLER_POLICY public setting (per-crawler groups; search crawlers unaffected). The legacy “automated pings to Google and Bing” doc claim was corrected: Google deprecated its sitemap ping endpoint in June 2023.

  • Security-utils optimization pass, benchmark-driven (October 2026): new pure-CPU baseline benchmark tests/benchmarks/security-utils.test.ts (33 cases over 13 hot per-request modules, JIT-warmed, registered in the CI matrix) drove a mechanical optimization sweep over src/utils/security/ with behavior locked by two new equivalence suites (40 tests, incl. Unicode case-folding edges). Measured (same session, A/B): containsXssVector clean 0.10 → 0.015 µs (~6×), sanitizeString clean 0.10 → 0.02 µs (5×), sanitizeObject clean 0.26 → 0.09 µs (2.9×), permission-cache.get hit 0.21 → 0.10 µs (2.1×) / miss 0.18 → 0.06 µs, sanitizeMongoQuery 0.43 → 0.35 µs (−19 %, p95 −24 %), readSessionIdFromCookieHeader 0.11 → 0.05 µs — via a module-level /[<:=]/ triage gate, ASCII-exact short-circuits for script/iframe/handler/URL classes (the lowercase copy is now paid only by case-folded URL schemes), WeakMap-memoized permission-cache keys, and lazy dotted-path building in the Mongo sanitizer. The loop also caught and reverted two changes that measured slower (a for…in strip loop was 2.3× slower than Object.keys(); a handler-only pre-check regressed script-tag payloads) and proved a suspected sanitizeString regression to be session noise via an isolated in-process A/B. Nothing weakened: sequential strip passes (a merged-regex rewrite diverged on nested tags in fuzzing), constant-time compares, CSPRNG, and TTL staleness guarantees are unchanged.

  • Rate-limit hot-path lean-out (October 2026): the per-request mutation limiter (src/utils/rate-limit/) no longer allocates Date objects (scratch-Date slot math in request-clock, local-time/DST semantics bit-identical), no longer re-inserts bucket keys into the Map on every touch (LRU position refreshed every 16th touch; lastActiveMs-based prune unchanged, eviction-victim staleness bounded and documented), no longer concatenates prefix strings per endpoint-cost entry (allocation-free boundary-char checks), and computes Date.now()/role-tier resolution once per request in the orchestrator. config.ts memoization was deliberately skipped — tests mutate process.env mid-suite, and the production hook passes an explicit base anyway. Locked by 9 new memory-store tests; full unit suite green (5 550 tests). Follow-up replica run (competitive-workload-replica, 100k docs, SQLite, production-mode server): findById 13.1k RPS warm 8c, listPlain 10.0k, findMissing 11.4k, create 1.5k / update 1.4k, seed 26.4k docs/s — absolute numbers for the record; not directly comparable to the external Postgres harness rows.

  • In-Context Live Preview: Visual DnD, pulses, and editable highlights within the live site frame (Editable Website plugin).

  • AI-as-a-Field-Type: Native AIEnrichment widget and LLM-driven Smart Translation.

  • Agent-Ready WebMCP Protocol (April 2026): Native content.ts WebMCP integration offering “Draft-by-Default” execution buffers and schema-aware, token-optimized context payload generation to power external Autonomous Agents.

  • Multi-Cloud Storage Engine: Universal strategy adapters for S3, R2, and Cloudinary.

  • Hosted MCP Server: Model Context Protocol server live at mcp.sveltycms.com.

  • Active Visual Workflow Engine (FSM): Production-ready Node-based editor with strict RBAC transition guards, secure crypto IDs, and true tenantId isolation across all workflow logic.

  • Enterprise Smart Diff View: Hardened diff-utils implementation performing recursive zero-latency change analysis, protected against circular DOSt vulnerabilities and optimized with Svelte 5 $derived metrics.

  • Multi-Tenant Hardening (July 2026): Production-hardened isolation across all layers:

    • PostgreSQL Row-Level Security: Database-level enforceTenantPolicy() with CREATE POLICY tenant_isolation and per-request SET SESSION app.tenant_id. Defense-in-depth below the application layer.
    • Signed Media URLs: HMAC-SHA256 time-limited file access with crypto.timingSafeEqual validation. Opt-in via MEDIA_SIGNED_URL_ENABLED.
    • Per-Tenant Rate Limiting: Two-tier token bucket system (per-IP + per-tenant) with independent budgets, preventing noisy tenants from starving neighbors.
    • Tenant Analytics Dashboard: Per-tenant usage widget showing storage, users, collections, entries, and 24h activity — scoped by locals.tenantId.
    • Tenant-Scoped API Keys: Token management UI with tenant scope selector (Current Tenant / Global). Backend already supports tenantId in auth cache lookups.
    • Per-Tenant Database Connection Pooling: Optional dedicated postgres.js pools per tenant with configurable TENANT_DB_POOL_SIZE. Visible as application_name = tenant_{id} in pg_stat_activity.
    • E2E Isolation Tests: 5 Playwright tests verifying cross-tenant collection, file, content, global resource, and schema boundaries at the HTTP level.
    • 19/19 architectural features covered across CRUD guard, RLS, signed URLs, rate limits, GraphQL, file serving, media, plugins, SSE, batch ops, migration, and E2E testing.
  • Durable Jobs & Webhooks: Redis-backed job queue (svelty_jobs) and DLQ-aware webhook engine.

  • Asynchronous Background Imports: Off-thread processing for large datasets with concurrency control.

  • Real-Time Collaboration: CRDT-based concurrent editing (Yjs) over standard HTTP.

  • Formal Server-Side SDK: Native locals.cms for zero-latency SvelteKit integration.

  • Essential Utilities: Standardized high-performance cn, pluralize, and slugify utilities for robust UX foundation.

  • Adaptive Job Scheduler: Lightweight background job runner with adaptive polling and audit integration for scheduled publishing.

  • Crypto-Chained Audit Logs: SHA-256 tamper-evident audit trail with chain verification and per-content viewer.

  • Per-Field Localization: Record field storage with inline locale switchers and one-click AI translation.

  • Flexible system languages (September 2026): Admin UI ships EN/DE. Setup and System Settings use the same ISO 639-1 picker as content languages (RTL via getTextDirection() in @utils/string). Completing setup writes project.inlang/settings.json and runs bun translate + bun run paraglide. Guide: System Languages.

  • EU + Arabic language pack seed (September 2026): Catalogs for FR, ES, IT, NL, PL, and AR clone English with a reviewed chrome overlay (scripts/seed-language-packs.ts); project.inlang/settings.json lists all eight locales. Remaining keys stay English until human review. Arabic sets html dir="rtl" via getTextDirection.

  • AI-Assisted Builder Phase 1 (September 2026): POST /api/ai-builder/approve-collection validates a design proposal, writes a TypeScript collection schema via the compiler AST (generateCollectionSourceFromProposal), compiles with syncContentState, and audit-logs the approval. Draft-gated (status: "draft"); refuses overwrite unless overwrite: true. Admin-only, tenant-scoped.

  • Smart Table advanced UX (September 2026): Multi-column sort (sort=title:asc,createdAt:desc + QueryBuilder.orderBy), advanced AND/OR filter modal (filterLogic + orWhere), and ATAG keyboard grid navigation (arrow keys, Home/End, Space, Enter) on collection entry lists.

  • Marketplace remote install (September 2026): POST /api/marketplace/install downloads remote packages through validateEgressUrl/safeFetch, verifies SHA-256 checksums, allowlists install paths, and activates licenses via POST /api/marketplace/license. In-app Install / license / package-detail UI on Extensions → Marketplace.

  • Standalone DB adapter npm packages (September 2026): sveltycms-sqlite, sveltycms-mongodb, sveltycms-mariadb, and sveltycms-postgresql re-export the in-tree adapters (no dual engine). CI workflow .github/workflows/publish-adapters.yml publishes on v* tags.

  • Zero-Dependency Full-Text Search (adapters): Database-native FTS adapters implemented across PostgreSQL, MariaDB, SQLite, and MongoDB with no additional npm packages; not yet wired into the live search path (see roadmap).

  • AI-Smart CMS Migration: 5-format importer with heuristic field mapping, ACF/CMB2 detection, and drag-and-drop UI.

  • Quick-Start Collection Templates: 7 setup presets — Website Starter (default) plus Blog, Agency, SaaS, Corporate, E-commerce, and Blank.

  • Website Starter Test Suite (July 2026): Unit, integration, and E2E smoke coverage for Svedit helpers, homepage seed, license gate, and public / route.

  • Progressive Initialization UX: Synchronous middleware init — zero-latency cold start without intermediate pages or polling, saving system resources.

  • Behavioral Learning Engine (June 2026): Server-side access pattern tracking with exponential decay scoring, driving adaptive cache warming, smart prefetch, and automatic dashboard reordering.

  • AI Widget Scaffolder (June 2026): On-demand code generation for custom 3-pillar widgets from a config object.

  • Hosted Marketplace (June 2026): Plugin/widget ecosystem at marketplace.sveltycms.com with REST API, Stripe checkout, and License v1 verification.

  • Marketplace Phase 2 — In-App Catalog & Dashboard Widget Packages (August 2026): Extensions → Marketplace tab with GET /api/marketplace offline-first merge; dashboard widgets refactored into marketplace-portable package folders (widgets/<folder>/ + required widget.json + .mdx); installDashboardWidget(); local catalog listings with license/price badges. Server-side license gates for premium dashboard endpoints (dashboard-license.ts → 403 LICENSE_REQUIRED, 14-day key-less trial) alongside client-side upgrade prompts — one Free / Freemium / Paid model shared with widgets, plugins, and the site starter. 22/22 core dashboard packages validated by scripts/check-dashboard-widget-packages.mjs (manual: bun run lint:widgets), including freemium Commerce Orders and Commerce Inventory (14-day trial).

  • Dashboard first-paint lean-up (August 2026): /dashboard no longer eager-evals all 22 widget Svelte modules for picker metadata (widget.json via getInstalledDashboardWidgets() + manifestsToPickerList()); saved layout hydrates in +page.server.ts so the client skips /api/system-preferences on first paint; widgets are optional — Svelte chunks load only when added or visible (no first-6 prefetch); plugin-gated packages (requiresPlugin, e.g. Commerce Orders / Inventory) stay out of the picker until that plugin is enabled; AI GenerativeDashboard / json-render-svelte load on demand; base-widget pauses polls while document.hidden and dropped _=Date.now() cache-bust query strings. Helper coverage: tests/unit/dashboard/dashboard-runtime.test.ts.

  • Access management lean-up + fail-closed RBAC writes (August 2026): /config/access-management stays admin-only; POST /api/user/update-roles is denied to user:write editors at the dispatcher and handler; website-token mutations require admin; unused access.remote.ts query("unchecked") token RPCs removed; tabs (permissions/roles/admin/tokens) load on demand; role/permission SSR payloads are field-allowlisted; token user picker drops password/2FA fields. Coverage: tests/unit/api/user.test.ts, tests/unit/api/dispatcher-security-matrix.test.ts, tests/unit/routes/access-management-page-server.test.ts, tests/unit/config/website-tokens-api.test.ts.

  • Media gallery → image editor lean-up (August 2026): /mediagallery grid/table previews use mediaDisplayUrl() (thumbnail/sm/md) instead of the original file; SSR rows are toGalleryListItem() allowlists; Sharp/MediaService is not imported for gallery load(); ImageEditorModal / details / table / advanced search / tag editor / drag-preview / folder prompt load via import() on use; published-in-use gating is one getPublishedReferencedIds() pass (coalesced index rebuild) instead of N parallel isReferencedByPublishedContent() scans. Coverage: tests/unit/media/media-utils.test.ts, tests/unit/media/media-reference-index.test.ts, tests/unit/media/gallery-list-item.test.ts.

  • Sharp handling: safer, leaner, faster (2026-09-25): one shared getSharp() loader (src/utils/media/sharp-loader.server.ts, memoized + retryable, raise-fail-fast with SHARP_LOAD_FAILED, typed SharpFactory) replaces five local loaders and two inline import("sharp") sites (image-processor, media-processing, media-service, media-storage, smart-importer, hooks.server.ts boot tuning); the shared MAX_INPUT_PIXELS (100 MP) bomb guard now covers every pipeline — including manipulateMedia and the smart-importer, which previously had no guard; JPEG variants drop mozjpeg: true — measured 9.2× faster at +26 % bytes (tests/benchmarks/sharp-vs-bun-image.test.ts, JPEG 1920 (sharp, mozjpeg OFF) row; editor q92 output keeps mozjpeg deliberately). Validated: 267 media/plugin unit tests, SQLite integration 689/689, and the media audit + upload-stress benchmarks on a fresh harness build. Same-day follow-ups (2026-09-25): widget multi-uploads now save through an order-preserving bounded pool (cap 4, process-upload.ts, coverage tests/unit/widgets/core/media-upload-process.test.ts); SVELTY_JPEG_MOZJPEG=1 is a deployment opt-in that restores mozjpeg for byte-critical hosts. Measured and deliberately not shipped: decode-once ladders (JPEG −8 % vs shrink-on-load re-decode, PNG ≈ 0) and WebP effort 0/1 (≤ noise, +1.9–5.7 % bytes) — rows in sharp-vs-bun-image.test.ts. Deferred-variant coalescer (2026-09-25): variant-job-scheduler.server.ts gives the deferred preset pipeline single-flight per (tenant, hash) plus a FIFO queue capped by SVELTY_VARIANT_JOBS (default 2) — concurrent identical uploads share one pipeline run and batch imports queue in waves instead of saturating libvips/memory; coverage tests/unit/media/variant-job-scheduler.test.ts (5 tests), full unit 557/557.

  • Plugin Admin Pages (page part, August 2026): plugins contribute full admin pages under /plugin/<path> (catch-all route + isomorphic registry + server load + RBAC 403 gate) with declarative sidebar nav, plus the <AdminZone> renderer that activates the previously dormant adminTool and AdminAreaExtension registries (sidebar/header/footer/toolbar/dashboard/config/content zones).

  • JPEG is the default derivative format (2026-09-27): the encoder, not the resize, dominates the upload ladder, and the effective default was split — the setup seed wrote { format: "webp" } while saveResized() fell back to original. Both now resolve to JPEG through one shared normaliser (DERIVATIVE_FORMATS/normalizeDerivativeFormat() in src/utils/media/media-models.ts, jpg as the historical alias, unknown value ⇒ jpeg), so the SIZES ladder writes one file per step instead of a WebP primary plus a WebP sidecar. webp/avif stay one setting away (MEDIA_OUTPUT_FORMAT_QUALITY.format), and original keeps the old source-format + WebP-sidecar pairing as an explicit opt-in. A source with an alpha channel keeps WebP rather than being flattened onto the JPEG encoder’s background. Original uploads stay byte-for-byte, the 32 px LQIP keeps its own WebP rule, and blueprint-level mediaConfig.convertTo (smart-importer) still wins over the global default. Coverage: tests/unit/media/processing/media-storage.test.ts, tests/unit/media/derivative-ladder.test.ts; measured cost table (self-measured 2026-09-27: JPEG 1920 40–57 ms, 4-step ladder 40–57 ms, AVIF 640 102–143 ms) in media-system.mdx.

  • Zero-Allocation Driver & GraphQL Fast-Path Acceleration (August 2026): Static ESM binding for hot API domain handlers (collections, content, auth, system, tokens, media, dashboard) eliminating dynamic-import resolution on 95%+ of traffic; bounded FIFO route resource state machine (1024 entries); zero-allocation SQLite/relational query parameter coercion (direct array reuse when values need no conversion); pre-compiled fast-JSON envelope serializers; and expanded single-collection GraphQL fast-path supporting filters and sorts directly via LocalCMS (bypassing Yoga GraphQL AST generation). Verified with reproducible benchmarks: direct adapter reads at 0.013ms (48.4k RPS), LocalCMS SDK calls at 0.017ms (54.9k RPS), cached counts at 0.003ms (145.8k RPS), and GraphQL collection throughput of 759 RPS.

  • Cache 2026 Next-Gen Architecture & Multi-Node Cluster Suite (Late August 2026):

    • GraphQL Automatic Persisted Queries (APQ): Implemented standard Apollo APQ protocol (extensions.persistedQuery.sha256Hash and GET ?extensions= / ?hash=) with fail-open handshake (PERSISTED_QUERY_NOT_FOUND) and L1 RAM / L2 Redis query registration (7-day TTL), eliminating AST compilation overhead on cached queries.
    • AI-Driven Speculative Pre-Warming: Integrated routeResourceStateMachine with the Behavioral Learning Engine (predictNextPath Markov transitions). The highest-scoring transition triggers non-blocking, single-flight pre-warming of target endpoints before the user clicks.
    • Zero-CPU Pre-Compressed Binary Byte-Serving: responseCache asynchronously computes Brotli and Gzip binary chunks (Uint8Array) for responses $>1\text{ KB}$. TURBO-HIT and GraphQL hit paths serve binary chunks directly with zero on-the-fly CPU compression during traffic spikes.
    • $O(1)$ Circular Ring-Buffers: Converted cache metrics buffers to fixed $O(1)$ circular ring buffers, eliminating continuous 1,024-element GC array re-allocations on every request.
    • Empirical Validation: Benchmarks verified Local SDK at 0.004 ms (195.2k RPS), HTTP E2E at 0.440 ms (2,262 RPS), GraphQL peak stress capacity at 4,785 RPS (100 concurrent connections), and Direct findOne at 0.006 ms (167.3k RPS).
  • Middleware Pipeline Streamlining & Tag-Based Invalidation (August 2026): Pruned mutation-only hooks (handleRateLimit, handleAuditLogging) from API_READ (GET) and token-resolution from API_WRITE; merged handleLocalSdk + handleContentInitialization into atomic handleLocalContext; unified AEO headers directly into applyAllSecurityHeaders eliminating standalone middleware; tag-based cache invalidation across all 4 database adapters (res:graphql, res:col:*) resolving GQL-after-write caching; and $state.raw frontend table runes. Verified via tests/benchmarks/hooks-performance.test.ts: REST with API Caching latency reduced to 1.27ms (604 RPS, +11.2% throughput), mutation latency reduced to 2.10ms (-14.3%), and p95 auth pipeline down to 4.34ms.

  • LiteRT.js AI Client (July 2026): Client-side browser ML inference via LiteRT.js in an isolated Web Worker — WebGPU primary, XNNPACK fallback, transparent Ollama fallback. First use case: alt-text generation (ai.generateAltText()) for direct WCAG/ATAG compliance. CSP-safe — admin page security headers never modified. SvelteKit remote function API (import { ai } from \"@services/ai-client\"). Dedicated /ai/worker endpoint with isolated WASM-allowing CSP. 13 files, 3K+ lines, 100% TypeScript, 4 unit tests.

  • Unified Data Hub v2.4.0 (REST write-back): All 5 connector types (Postgres, MariaDB, SQLite, MongoDB, REST) support read+write. REST writes are opt-in (writesEnabled), SSRF-safe, with Idempotency-Key dedup and circuit breaker. GraphQL mutations (createVirtualEntry, updateVirtualEntry, deleteVirtualEntry) for headless clients. Full API parity across LocalCMS, HTTP, GraphQL, and WebMCP agents. 96 unit tests.

  • Unified Data Hub v2.2 (stable): Marketplace plugin — Postgres + REST federation, same-source joins, native stitch enrich, Collection Builder enrichment picker, GraphQL/WebMCP parity, per-source cursor pagination, decomposition telemetry, headless contracts, cross-source alpha (opt-in). See unified-data-hub.mdx.

  • E2E Architecture Simplification: Collapsed 20 Playwright projects → 4 (wizard, firstuser, auth-setup, chromium). Reduced CI jobs from 19 to 6, server startups from 19 to 1. Total E2E time ~70–110 min → ~12–15 min.

  • Intelligence Consumption, Predictive Navigation & Smart Table Saved Views (September 2026):

    • Behavioral Navigation & Collection Tree Smart-Tag: Confidence-gated adaptive prediction (predictNextPathAdaptive) eliminates cold-start guesses; <Collections /> and TreeView render polymorphic <a> links with data-preload="smart" for the server-predicted collection destination alongside pinned and plugin links.
    • “Warm Next Page” on Smart Tables: Converted table pagination controls (pagination.svelte) to predictive links; the Next Page button uses data-preload="viewport" to trigger automatic route pre-warming (/api/system/prewarm-route) and data preloading via IntersectionObserver before click.
    • Heat-Aware SWR & Dashboard Quick Chips: Top-20 hot collections keep a 60s fresh window but serve stale up to 15 min with background revalidation; the admin dashboard surfaces quick-access chips from hotCollections.
    • Smart Table Saved Views 100% Platform Completion: Extended SmartTableSavedViewsMenu to Media Gallery (mediagallery/+page.svelte) and User Management (admin-area.svelte), achieving 100% coverage across all 4 list surfaces (Collections, Website Tokens, Users, and Media Gallery).
    • SSR & Pipeline Lean Execution:
      • Zero Redundant User Reads: Synchronized userCount between authz hook and SSR layout cache; seeded layout:user:${_id} upon auth resolution, eliminating secondary DB user queries on layout render.
      • Content Structure SSR Fast-Path: Eliminated unused getNavigationStructure execution from contentPromise and added synchronous L1 retrieval for cached sidebar structure.
      • GraphQL AST Re-Parse Elimination: Stashed pre-parsed GraphQL AST from handle-security.ts complexity checks onto event.locals.__graphqlAst and wired into GraphQL Yoga’s onParse, avoiding duplicate AST parsing on /api/graphql POST requests. Peak throughput reached 4,563 RPS.
  • Admin Session Console & Smart Table List Platform Expansion (September 2026):

    • Admin Session Console UI: Shipped dedicated ModalUserSessions dialog in User Management (admin-area.svelte) allowing administrators to inspect active sessions per user (device type, browser, OS, client IP, last active timestamp) and revoke individual sessions or all other sessions with CSRF protection.
    • Status & Role Facet Chips: Added interactive facet chips above the users table (All, Admins, Users, Active, Blocked) with sub-millisecond filtering and dynamic state management.
    • List Query Observability: Instrumented recordListQuery across /api/user and /api/token and connected SmartTableMetricsBadge to user management and token tables for real-time p50/p95 latency and hit-rate telemetry.
    • Filter Engine Reuse: Added allowlisted field filtering (role, blocked, email, username, createdAt) across auth.ts and AuthNamespace.listUsers.
  • Adapter-agnostic speculative Turbo fill & 100k write-burst integrity (September 2026):

    • Functional loop (measured): hover/predict/viewport/smart → GET /api/system/prewarm-route → LocalCMS findById/find (all 4 adapters) → {success,data} stringified once → responseCache.buffer (Uint8Array) → next collection GET returns X-Cache: TURBO-HIT (zero DB, no per-request JSON.stringify). Proven by tests/benchmarks/speculative-turbo-hit.test.ts.

    • What this is not: no frozen radix trie / O(1) segment-offset dispatch (classifyRouteSpec is a 15-entry exact Map + startsWith + 1024 LRU). Prediction is predictNextPathAdaptive (≥5 samples and 30% accuracy, not an 82% example). Extinction is 0.8 on non-chosen alternatives at navigation time, not a 10s timer. Transition cap is 10,000 per tenant (prediction-stats cap is 5,000). Turbo GET still goes through SvelteKit Response + security headers — JSON/GC cost is gone; there is no raw TCP-socket bypass.

    • Write-lane TTL slide: getTurboAuthContext slides expiry on every hit; the collection write lane falls through to full auth instead of 401 when turbo is cold. Node headersTimeout on index.cjs / index.bun.ts is 10 minutes (was 60s 408 with no CMS log). That pair aborted 14/100k Postgres seed rows at ~60s.

    • Seed integrity: HTTP seed bursts retry 401/408/429/503/socket drops and fail if created !== requested. Verified BENCH_DOCS=100000 competitive replica on PostgreSQL: 100,000/100,000 seeded, 87s wall (self-measured 2026-09-02).

    • Scale-spectrum ledger (executed): exportResult per step + printTruthTable; BENCHMARK_RECORD=1 wrote history.sqlite (test_id=scale-spectrum) and MDX ledgers. Self-measured 2026-09-02, 1-row vs largest-step retention:

      Engine Steps findByIdRandom listFilterSort GraphQL Create
      SQLite (entry) 1→10k 2271→1581 RPS (0.70×) 2444→4602 (1.89×) 2047→3873 (1.89×) 1474→2540 (1.73×)
      PostgreSQL 1→100k 2379→1372 (0.57×) 2185→4410 (2.03×) 1923→3294 (1.71×) 1084→1482 (1.40×)
      MariaDB 1→100k 2303→1366 (0.60×) 2124→4329 (2.04×) 2098→4021 (1.91×) 1132→1002 (0.90×)
      MongoDB 1→100k 2420→1905 (0.78×) 2193→3971 (1.81×) 2179→3085 (1.41×) 1370→2521 (1.84×)

      LIMIT/PK paths stay flat-to-faster with N. Random PK retains 57–78% at 100k (B-tree depth + cache misses), not an O(N) collapse. MariaDB create at 100k is the only write dip (0.90×).

    • SQLite mixed trend (not a new cliff): ledger 🔴 +34% seq / +45% con vs the 7-run mean (~0.43 / 1.89 ms). This session’s pre-change baseline was already 0.47 / 2.80 ms; post-change re-runs sit at 0.58 / 2.74–3.47 ms. Mixed is write-growing (1/10 creates) and runs last after 300 creates + 300 updates, so it is noisier than isolated lanes. Not treated as a product regression.

    • Bootstrap skip: routeSpec.skipMiddlewares is honored (preferences / media) instead of being classified and ignored.

  • Point-lookup slab & hot-path serialization (September 2026):

    • Random-access slab cache: Implemented dedicated 2,000-slot FIFO cache (randomAccessSlab, 30s TTL) for point lookups, automatically invalidated on node/collection mutations. Boosted warm findByIdRandom from 1,488 RPS to 4,404 RPS (+196% cold-to-warm boost), bringing random point-reads to parity with sequential findById (4,643 RPS).
    • LRU lock contention elimination: Disabled updateAgeOnGet on ENTRY and CONTENT categories in cache-service.ts, removing unnecessary LRU reorder lock overhead on read-heavy hot paths.
    • Pattern invalidation acceleration: Invalidation latency reduced by −87% (4.11ms → 0.53ms, +676% RPS, 243 → 1,884 RPS).
    • Single-pass O(N) array serialization: Refactored serializeArrayFast in src/utils/fast-json.ts into a single-pass join ([${out.join(",")}]), avoiding multi-slice concatenation and GC overhead on large array payloads (listLarge reaching 3,538 RPS in SQLite replica).
    • High-concurrency pool floor: Increased base connection pool floor from 20 to 32 on 8c+ hardware profiles (src/databases/postgresql/adapter-core.ts), preventing connection pool starvation under 32 concurrent clients.
    • Transactional mail pool hardening: Enforced maxRecipients: 1, maxMessages: Infinity, connectionTimeout: 10_000, and socketTimeout: 30_000 on pooled Nodemailer transports (src/utils/email.server.ts), guaranteeing forward-compatibility with Nodemailer 10.0.0.
  • Lean Architecture, Route Centralization & Storage Deduplication (September 2026):

    • Catch-All Gatekeeper Centralization: Eliminated rogue standalone API routes (/api/seo/link-suggestions, /api/chat, /api/remoteVideo), consolidating their logic into the central dispatcher (src/routes/api/[...path]/+server.ts) and handlers/utility.ts. All endpoints now strictly enforce central ENDPOINT_PERMISSIONS, fail-closed defaults, and RBAC matrix validation.
    • Storage & Connection Pool Deduplication: Re-architected src/utils/temp-store.ts to delegate directly to cacheService (CacheCategory.SESSION), eliminating duplicate Redis client instances, redundant pool allocations, and manual Map fallbacks.
    • Dead Code & Micro-File Pruning: Eliminated ~1,200 LOC of dead code across 12 obsolete files (file-uploading.ts, security.worker.ts, fs-mock.ts, path-mock.ts, response-headers.ts, field-selection.ts, memory-optimizer.svelte.ts, schema-diff.ts, server-utils.ts, system-integrity.ts, migration-manager.ts, lazy-rune.ts), eliminating micro-file fragmentation and legacy bridges.
    • Shared Logic & Security Hardening: Consolidated email normalization with Unicode NFC normalization against homoglyph attacks; unified database connection string parsing in Setup with @utils/connection-parser; wired client Save-Data and connection quality detection into @utils/predictive-preload.
    • AGENTS.md Contract Compliance: Enforced raise() error handling contract across preview endpoints and eliminated catch (err: any) in favor of typed catch (err: unknown) with safe stringification.
  • Enterprise Review Workflows, Visual Block Builder & In-App API Playground (September 2026):

    • Multi-Stage Review Workflows: Shipped complete content lifecycle FSM (WorkflowService) with RBAC-gated stage transitions (workflow:transition), audit history logging, collection binding, and visual state editor at /config/workflows. Editors transition entries with optional review comments directly inside the content view via <WorkflowActions>.
    • Visual Polymorphic Block Builder Widget: Shipped custom BlockBuilder widget (src/widgets/custom/block-builder/) enabling component-driven page design with 8 production presets (Hero, Text & Prose, Media Showcase, Call to Action, Features Grid, Quote/Testimonial, FAQ, Rich Text), Valibot schema validation, keyboard-accessible reordering, duplicate/delete operations, and framework-agnostic JSON output for modern headless frontends.
    • In-App Developer & API Playground: Shipped unified developer console at /config/api inside <AdminPageShell>, providing live REST execution against the OpenAPI 3.1 specification, interactive GraphQL query testing against /api/graphql, and multi-language client code generation (cURL, TypeScript fetch, Python requests, zero-latency SvelteKit LocalCMS).
  • Enterprise OIDC / SSO with RFC 7636 PKCE & JIT Role Mapping (September 2026):

    • RFC 7636 PKCE (S256): Cryptographic Proof Key for Code Exchange support with high-entropy code verifier generation and SHA-256 code challenge computation on every authorization code login flow (buildOidcAuthorizationUrl and exchangeOidcCode).
    • Just-In-Time (JIT) Auto-Provisioning & Claim Role Mapping: Automated new user registration on successful OIDC callback, mapping IdP claims/groups (e.g. groups, roles) to local CMS roles (resolveJitRole), with optional role synchronization on each login.
    • Dynamic Branded IdP Login Buttons: Front-end login screen (/login) now dynamically discovers and renders branded IdP login buttons (Google Workspace, Microsoft Entra ID, Okta, Auth0, Keycloak) with official icons, accessible ARIA labels, and predictive hover preloading.
    • SSO & OIDC Management Console: Dedicated tab in Access Management (/config/access-management) with provider presets, issuer OIDC discovery validation, redirect URI allowlisting, and granular JIT claim rule management backed by /api/auth/sso-providers.
  • Enterprise Collection Builder, Split-View & Database Schema Ingestion (September 2026):

    • Smart Type Inference & Natural-Language Quick-Add: Built a zero-dependency client-side heuristic engine (src/routes/(app)/config/collectionbuilder/smart-inference.ts) enabling instant natural-language field creation (e.g. email, price, cover_image, tags, is_published, author_id, rating, birth_date). Automatically resolves widget types, icons, database field names, and pre-configured validation constraints on Enter ↵, eliminating tedious modal clicks.
    • Dual-Pane Live “Visual Canvas ↔ TypeScript Code” Split-View: Implemented real-time bi-directional code ↔ GUI awareness in collection-widget.svelte with a 3-way view toggle (Canvas | Split View | TypeScript). In Split View, visual canvas edits reactively reflect in the generated production-ready defineCollection({ ... }) TypeScript schema (collection-code-generator.ts) with zero disk overhead, 100% parity with SveltyCMS AST compilation, and 1-click clipboard export.
    • Database DDL & Schema Ingestion Engine: Engineered a server-side schema-ingestion engine (schema-ingestion.ts, executed through the ingestSchema SvelteKit server function) and modal-schema-ingestion.svelte, enabling instant reverse-engineering of SQL CREATE TABLE definitions across PostgreSQL, MySQL, MariaDB, and SQLite directly into SveltyCMS collections. Automatically resolves FOREIGN KEY ... REFERENCES other_table(id) and *_id columns to Relation widgets (matched against the live collection list), ENUM(...)/CHECK constraints to populated Select widgets, JSON/JSONB to JsonEditor, arrays to Tags/Repeater, and parses arbitrary REST API JSON payloads into structured collections.
    • Tree-View Hierarchy & Drag-and-Drop Hardening: Corrected RTL off-screen coordinate mapping in tree-drag-preview.svelte with floating nesting targets (↳ Inside {category}), added live collection/category statistics, and implemented full keyboard reordering (Alt+Up/Down) with roving tabindex.
    • Unified Favorites & Tagging System with LeftSidebar Integration: Introduced collectionMetadataStore with Svelte 5 runes ($state), deterministic WCAG 2.2 AA token-based tag color palettes, and cross-component/cross-tab synchronization. Enables 1-click starring (★) of collections and categories in both the builder board and the left sidebar, color-coded tag taxonomy, dual-toolbar filtering chips (Favorites and All Tags), in-form tag configuration, and instant navigation from the sidebar header.
  • Per-Role Multi-Factor Authentication Enforcement (MFA-RBAC) (September 2026):

    • Role-Level MFA Requirement: Added Role.mfaRequired attribute with full database schema support across all 4 database adapters (SQLite, PostgreSQL, MariaDB, MongoDB).
    • Session AMR Lifecycle Tracking (RFC 8176): Sessions capture Authentication Methods References (amr: ["pwd"] vs amr: ["pwd", "mfa"]) and mfaVerifiedAt timestamps across in-memory and Redis stores.
    • Centralized Endpoint Gating: Requests made under mandatory-MFA roles carrying non-MFA sessions are rejected at the gateway (_checkEndpointPermission) with HTTP 403 MFA_REQUIRED.
    • Zero-Bypass Device Policy: Device trust cookies (__Host-2fa-trusted-device) are strictly overridden for roles with mfaRequired: true, mandating interactive second-factor verification on every session.
  • Token-Bucket Rate Limiter with Redis-Fallback & Adaptive Throttling (September 2026):

    • Continuous Token Replenishment: Replaced coarse fixed-window counters with a high-precision Token-Bucket algorithm (handle-rate-limit.ts) for smooth burst management, eliminating 2x spike artifacts at window boundaries.
    • Redis-Primary with Automatic Memory Fallback: Distributed token accounting via atomic Lua script on Redis L2 with a strict 15ms timeout and 5-second circuit-breaker backoff. Falls back seamlessly and instantaneously to local in-memory token buckets without failing requests or degrading user latency.
    • Zero-Tax Adaptive Sizing: Dynamically scales bucket capacities based on user profile (Admin 10x, Authenticated User 2x, Anonymous 1x) and system hardware pressure from SystemMonitor (0.8x–2.0x), utilizing a zero-overhead synchronous session cache peeker in handle-authentication.ts.
    • Self-healing / predictive / cost-aware upgrades (A+B+C): system-pressure.ts EWMA of process cpuUsage deltas (normalized by core count) plus 1-minute os.loadavg() when the OS reports it (ignored on Windows zeros — not hypervisor-specific). request-clock.ts 24h/15-min histogram for predictive throttle after warmup. endpoint-cost.ts longest-prefix token costs (/api/graphql 2, media upload 5, import 8, AI copilot 10). Admin tier never scaled down. Tests: tests/unit/rate-limit/*.test.ts.
  • Application-Layer Encryption (ALE) Zero-Tax Performance Benchmark (September 2026):

    • Empirical Benchmarking Suite: Created tests/benchmarks/ale-encryption-impact.test.ts profiling AES-256-GCM field encryption on content_nodes and audit_logs across 256 B, 4 KB, and 64 KB payloads.
    • Zero-Tax Cryptographic Optimization: Optimized field-encryption.ts with static crypto imports, synchronous key resolution (getFieldKeysSync()), direct GCM ciphertext extraction, and synchronous primitives (encryptFieldValueSync, decryptFieldValueSync).
    • Tolerance Gate Verification: Achieved sub-18 µs encryption latency (<3% of total write time), >213 MB/s memory throughput, and 0.20–0.55 ms transactional DB write speeds, confirming compliance with the <5% write overhead tolerance budget. Documented in Encryption Impact Benchmark.
  • Build Simplified Back to Plain Vite (September 2026):

    • scripts/build.ts removed: the separate build orchestrator (which had become broken on Windows — it imported a win32-adapter-node-patch module that never existed) is deleted in favour of the standard SvelteKit flow: "build": "vite build", with the documented build:node/build:bun/build:stats/build:analyze variants as one-line Vite invocations. Dev, CI and Docker all run the same single pipeline.
    • Its two real jobs moved into vite.config.ts as post-build plugins: bundleYjsSyncServerPlugin emits build/yjs-sync-server.js (esbuild, same options) for the /ws mount in index.server.mjs, and the module-worker copy already lived in Vite (copyWorkerFilePlugin). No shell wrapping, no duplicate entry point.
  • “Can We Do Better” Native-Utils Pass & WAF Rate-Limiter Consolidation (September 2026):

    • Strict RFC 9562 UUIDv7-only IDs: LUT-based validateId() (+45 % throughput, 1.83× faster rejection) plus zero-allocation generateUUIDv7 (10.2 M UUIDs/s, 98 ns/call vs 1.68 M npm uuidv7) replaced every crypto.randomUUID()/v4 site and fixture; timestamp-ordered keys remove PG/SQLite B-tree 50/50 page splits and WAL amplification. generateUUIDv4 deleted — no legacy tail.
    • WASM-free hashing: synchronous fastHash (5.22 M ops/s, 2.32× the WASM xxhash64) replaced hash-wasm in compile.ts and sync-content-state.server.ts; dependency removed, etag-hash.test.ts migrated.
    • WAF rate limiter on the native token-bucket engine: rate-limiter-flexible replaced by src/utils/rate-limit/ with windowMs fixed windows + overdraft parity, Redis Lua primary with memory fallback, prefix-scoped dump/restore, and a bot-UA short-circuit ahead of the limiter (deterministic 403 for known bot/scanner User-Agents on mutations — the integration security.test.ts UA/rate-limit pair stays green in full-parallel). Dependency removed.
    • Zero-allocation utilities: single-pass diff-utils/data-utils, FastLRU for local caches, generateUUID() in widgets (replacing Date.now()+Math.random() ID construction); nanoid/deepmerge-ts stay as security-pinned overrides for transitive deps.
  • Adapter Template-Cache Parity & Cross-Engine Test Hardening (September 2026):

    • MariaDB reaches SQL-template parity with PostgreSQL/SQLite: new FIFO-capped _mariaUpdateTplCache, _mariaUpsertTplCache and _mariaInsertReturningTplCache build the prepared SQL once per column shape (key includes column ORDER, tenant clause, merge mode, RETURNING choice), plus fail-closed assertSafeSqlIdentifier on the cached identifiers. MongoDB’s mapQuery operator table is now module-scoped — no per-condition allocation.
    • Measured honestly: a paired A/B on the live MariaDB container (same host, same session, alternated builds, untouched read lanes as controls) shows the caches are within the benchmark’s noise floor on single-op lanes (UPDATE 0.774→0.796 ms while control reads drifted −12–15 %) — the update lane is commit-bound, not statement-CPU-bound. The shipped value is structural: engine parity, per-call allocation elimination, and stable SQL text that lets mysql2’s server-side prepared-statement cache hit.
    • Cross-engine fixes found by the matrix: three contract tests wrote >36-char _id/user_id values that MariaDB/PostgreSQL reject (ER_DATA_TOO_LONG) while SQLite silently accepts (ids now ≤36 via shortId()); run-all-db-matrix.ts misread 5 failed from the passing test name “…after 5 failed login attempts” and now parses the harness Total/Pass/Fail report block; collection-widget.svelte regained the missing @thisux/sveltednd imports. Matrix re-run: all 4 engines PASS, 0 fail (sqlite 689, mongodb 687, mariadb 692, postgresql 695).
    • Auth hot-path date stamping now uses the memoized nowISODateString() / dateToISODateString() helpers (session AMR/MFA, magic-link, 2FA, session manager) instead of per-call new Date().toISOString() — rule §1 dates, one allocation per millisecond instead of per call.
  • Paraglide i18n Tree-Shaking Audit & Copy Scanner (September 2026):

    • Strict Tree-Shaking (Rule 3.1): Eliminated wildcard imports (import * as m from '@src/paraglide/messages') across dashboard widgets in favor of explicit named imports, preventing dead message bundle bloat.
    • Automated Hardcoded Copy Scanner (scripts/check-hardcoded-copy.ts): Introduced an automated scanner and CI check guarding against wildcard paraglide imports and untranslated strings across admin Svelte routes.
  • Enterprise Multi-Stage Review Workflows (September 2026):

    • Full Lifecycle Finite State Machine: Built WorkflowService with configurable stage graphs (draft → review → approved → published), audit history logging, and role-based stage transitions guarded by workflow:transition.
    • Visual State Graph Editor & In-Context Controls: Visual workflow editor at /config/workflows with collection binding, transition guards, and <WorkflowActions> embedded in the collection content view allowing editors to execute transitions with optional review comments.
  • Visual Polymorphic Block Builder (September 2026):

    • Component-Driven Page Layout Widget: Production widget (src/widgets/custom/block-builder/) featuring 8 standard presets (Hero, Text & Prose, Media Showcase, Call to Action, Features Grid, Quote/Testimonial, FAQ, Rich Text).
    • Interactive Ergonomics & Headless Export: Keyboard-accessible block reordering, duplication, collapse/expand, Valibot schema validation, and headless JSON export matching AST compilation standards.
  • In-App Developer & API Playground (September 2026):

    • Unified Developer Console: Integrated console at /config/api inside <AdminPageShell> providing interactive OpenAPI 3.1 REST execution, live GraphQL query testing against /api/graphql, and multi-language client code generation (cURL, TypeScript fetch, Python requests, zero-latency SvelteKit LocalCMS).
  • GDPR Article 17 Right to Erasure (September 2026):

    • Cascading Data Erasure: End-to-end permanent erasure at POST /api/gdpr (action: "erase" / "wipe") cascading across sessions, tokens, audit logs, and users via gdprService.eraseUser with strict tenantId isolation.
    • Verified Sub-Millisecond Benchmark: right-to-be-forgotten-audit.test.ts verified on production endpoint with <1ms avg wipe latency and 0 orphaned records across all four database engines.
  • Intelligence Consumption & Predictive Navigation (September 2026):

    • Behavioral Learner Preloading: Integrated predictNextPathAdaptive with confidence gating; applied data-preload="smart" to sidebar navigation and collection tree nodes in collections.svelte and tree-view.svelte.
    • Warm Next Page: Added viewport preloading on Smart Table pagination controls (data-preload="viewport" on next page link via pagination.svelte) and dashboard hot-collection quick chips.
    • Heat-Aware SWR: Implemented heat-aware stale-while-revalidate in collection-service.ts, serving hot top-20 lists stale up to 15 minutes while maintaining 60s fresh TTL.
  • Request-Pipeline Lean Optimization (September 2026):

    • Single L2 Cache Key: Unified hook and dispatcher caching under a single canonical L2 cache key, retiring duplicate namespaces.
    • Single-Compression Guarantee: Introduced a compressed: {} sentinel guaranteeing a single compression pass (Brotli/Gzip/Zstd) re-populating L1 variants.
    • Admin Sidebar Caching: Cached admin sidebar payloads per (tenantId, contentVersion) to eliminate per-SSR reconstruction and recursive cloning overhead.
  • Smart Table Saved Views Integration (September 2026):

    • Unified Saved Views UI: Wired SmartTableSavedViewsMenu across User Management (admin-area.svelte), Entry List, and Website Tokens surfaces.
  • Column Resize & Bulk Field-Edit Platform (September 2026):

    • Omni-Surface Column Resizing: Header drag-resizing and width persistence (ColumnResizeHandle + smartTable.getColumnWidthStyle) extended across all primary CMS data tables: Entry List (entry-list:{id}), Website Tokens (website-tokens-table), Media Gallery (media-gallery-table), and User Management (admin-area-users / admin-area-tokens via dynamic smartTable.setLayoutKey).
    • Bulk Field-Edit UI: Interactive batch edit modal (bulk-field-edit-modal.svelte) wired to MultiButton (Alt+E), resolving field types (text, number, boolean, date) and executing batch updates via POST /api/collections/:id/batch (action: "update").
    • Sticky Columns Hardening: Ensured pinned start (selection checkbox) and pinned end (actions) columns have solid, non-bleed backgrounds (bg-white dark:bg-surface-900) and pinned body rows across horizontal scrolling on wide tables.
  • Unified Upload Allowlist & Write-Time MIME Conflict Defense (September 2026):

    • Single Upload Allowlist: Derived isAllowedUploadMime() from MIME_MAP minus scriptable and office types (text/html, JS, XML, CSS, Office formats); used uniformly across streaming uploads, saveMedia, and the <MediaUpload> widget. Permissive media formats (HEIC, AVIF, FLAC) supported while eliminating insecure prefix matching (image/*).
    • Write-Time Polyglot Conflict Detection: Implemented assertMimeAgreement() running after magic-byte sniffing (first 512 bytes) on write. Allows same-family transitions (JPEG vs PNG) while strictly rejecting polyglots, scriptable payloads, or HTML masquerading as image extensions (MIME_MISMATCH), avoiding expensive head reads on GET requests.
    • Universal nosniff Defense: Enforced X-Content-Type-Options: nosniff globally on MEDIA_RESOURCE_HEADERS, ensuring local file delivery, cloud 302 redirects, share-downloads, and turbo static responses strictly reject browser MIME sniffing.
    • Serve-path resolve without mime-types/mrmime: /files and share-download use resolveMimeTypeFromPath (extension map → stored MIME → 512-byte sniff → application/octet-stream). Scripts use node:fs globSync instead of the glob package.
    • Remote/importer MIME: resolveRemoteAssetMime() is the shared path for smart-importer, S3 mirror PUTs, and saveRemoteMedia. Remote Content-Type: octet-stream is treated as unknown so sniffing can recover the type; HTML is sniffed (<!DOCTYPE html> / <html) and rejected against image claims. Image-editor watermark/blur/annotate IDs use generateUUID() (CSPRNG).
    • Verification: tests/unit/media/media-utils.test.ts, tests/unit/media/slim-sniffer.test.ts, tests/unit/media/processing/media-svg-ssrf-regression.test.ts.
  • CollectionsNamespace Consolidation & Hot-Path Architecture (August 2026):

    • Monolith Decomposition: Decomposed the ~2,100-line collections-namespace.ts into a lightweight orchestrator supported by focused modules (schema-store, read-pipeline, write-pipeline, post-write, request-cache, lazy-services).
    • Zero-Redundancy Schema Walks: Field prep consolidated to a single schema walk and single clone via prepareCollectionFields() in content-utils.ts, replacing 3 prior passes; unified applySchemaHookPipeline across create/update operations.
    • Physical Table Naming Reconciliation: Normalized adapter table naming (normalizeCollectionTableName()) across SQL (getTable, createIndexes, registerTableSchema) and MongoDB (normalizeCollectionName, createModel), resolving hyphenated-ID model registry inconsistencies.
    • Hot-Path Optimization: Inserted widget transform field-level caching, insert-only createdAt semantics, synchronous L1 mutation with background L2 fire-and-forget invalidation, and session verification via single JOIN.
  • Unified Translation Cockpit & Multilingual Studio Inspector (September 2026):

    • Dual-Lens Architecture: Live Paraglide admin chrome switcher coupled with content translation readiness metrics in translation-cockpit.svelte.
    • Automated Deficiency Resolution: Real-time completeness calculation, interactive jump-to-field focus, and 1-click AI document auto-translation. Tests: tests/unit/utils/translation-analytics.test.ts.
  • Dependency & Runtime Lean-Out (September 2026):

    • Zero-Lodash & Modular AWS: Full elimination of Lodash, lazy on-demand S3 client, and dynamic import of MapLibre GL in address inputs.
    • Svelte 5 Runes Footprint: Deletion of legacy store monoliths, 28 → 18 store consolidation, and removal of unused dependencies (shiki).
  • Smart Table Advanced UX & Adaptive Grid Platform (September 2026):

    • Multi-Column Sort & Ordinal Ranks: Composed multi-column sorting via Shift + Click with visual rank indicators (1, 2, 3), cascading client-side comparators, and query serialization.
    • Compound Filter Logic: Schema-aware AdvancedFilterModal with AND/OR multi-clause conditions and contextual operators.
    • Grid Accessibility & View Modes: WCAG 2.2 AA roving tabindex cell/row keyboard traversal, Alt + X selection export, and dynamic table / card view mode switching. Tests: tests/unit/components/smart-table-advanced-ux.test.ts.
  • Media Pipeline — Dedupe Order & Derivative Ladder (September 2026):

    • Dedupe Before Derivative Generation: The tenant-scoped content-hash lookup in MediaService.saveMedia() now runs before any resize work, so re-uploading identical bytes performs zero encodes and writes no derivative file. The reuse path verifies that the original and every derivative path the row advertises still exist (cache-bypassing lookups) and, when one is gone, repairs the row in place (original rewritten, ladder re-derived, metadata merged) instead of returning URLs that 404 or inserting a second row for identical bytes. The deferred responsive-variant job is not scheduled on a hit at all. Because nothing is rewritten, the W/"size-mtime" ETag survives a re-upload and clients stop re-downloading unchanged assets.
    • Derivative Ladder Clamped to the Source: saveResized() drops every SIZES step wider than the decoded source and passes withoutEnlargement to the encoder, so a 400 px icon is no longer enlarged into four derivatives; each resolved path is written once (an already-WebP primary no longer collides with its own sidecar), and the thumbnails map now records the encoder’s real output dimensions instead of the requested ones.
    • Shared reuse/repair helpers: findMissingStoredFile(), adoptExistingRecord(), repairExistingRecord() and buildDerivativeSet() replaced three near-identical dedupe blocks (SVG, buffered raster, streamed raster) with one contract.
    • Also fixed (found by the new assertion): enrichMediaWithUrl() re-prefixed thumbnail URLs that already carried /files/…, producing /files/files/… and making reuse responses differ in shape from insert responses.
    • Measured (real SQLite stack, bun run test:integration tests/integration/api/media-dedupe-derivatives.test.ts, 2026-09-22): 400 px source — 8 derivative files / 16,984 B / a 600 px upscaled variant → 2 files / 600 B / no upscaled variant; duplicate upload — 47.5 ms and 8 of 9 stored files rewritten → 3.0 ms and 0 of 5 rewritten.
    • Verification: tests/unit/media/derivative-ladder.test.ts, tests/unit/media/upload-dedupe-order.test.ts, tests/integration/api/media-dedupe-derivatives.test.ts; benchmark rows Media: Derivative Fan-Out (1920px|400px source) and Media: Duplicate Upload (identical bytes) in tests/benchmarks/media-performance.test.ts, with media.derivatives.* / media.duplicate.* metrics. Architecture and verification: Media System Architecture.
  • Partial-Update Data Loss Fixed — JSON data Merge on All Four Engines (September 2026):

    • The bug: crud.update() built one whole-column assignment for the JSON data blob (SET "data" = $n::jsonb on PostgreSQL, `data` = ? on SQLite/MariaDB), so a partial PATCH replaced every dynamic field it did not mention. Measured externally before the fix: 30,940 of 100,000 documents collapsed from a ~1.5 KB median to 61-byte stubs — and because their own findByIdRandom/graphql rows read ids[0], the corruption also inflated a cached-read benchmark number. MongoDB was never affected ($set merges per field), which is what fixed the target semantics: a shallow, null-keeping merge. A patched object replaces wholesale (no recursion), and null is a stored value rather than a deletion.
    • The fix: prepareValues() marks a partial dynamic-field payload as a patch (a WeakMap entry keyed by the prepared values — invisible to Object.keys, spreads and Drizzle .set()), and each dialect applies it at zero extra cost: PostgreSQL merges inside the UPDATE (COALESCE("data",'{}'::jsonb) || $n::jsonb), SQLite json_patch(...), MariaDB JSON_MERGE_PATCH(...). Both of those are RFC 7396 — recursive, and they delete keys patched with null — so patches containing nested objects or explicit null hydrate the stored blob and merge in JS instead (jsonPatchNeedsJsMerge()); scalar/array patches keep the single-statement fast path. The Drizzle fallback merges in JS too, so no path can silently fall back to the destructive write. Full-document writers (sync, seeds) opt out explicitly with replaceData: true, which skipReturning: true already implies — that path reconstructs the row from the prepared values, so a merge would misreport the untouched columns.
    • Fail-closed, not silent: if the blob for a JS-merge cannot be read (missing row), the update throws rather than persisting the patch as the whole document.
    • Verification: tests/unit/databases/json-data-patch-merge.test.ts (exactness boundary, blob decoding, marker invisibility) and four new cases in tests/integration/databases/adapter-parity.test.ts — partial patch keeps untouched fields, nested object replaces shallowly, explicit null is kept, and the replaceData opt-out. Run green on SQLite 21/21, PostgreSQL 21/21, MariaDB 21/21, MongoDB 20/20 + 1 intentional skip (replaceData has no MongoDB counterpart — its $set never replaced anything). The reported request shape is also covered end-to-end through the HTTP API (tests/integration/api/collections-mutations.test.ts: PATCH {views: 2} on a document with title/body must return and persist all three), green on SQLite and PostgreSQL.
    • Still to do (roadmap): none for the schema path — the spec is the only trail now.
  • Partial-Update Merge Extended to Every Bulk Write Path (September 2026):

    • Bulk paths merged: crud.updateMany, batch.bulkUpdate (homogeneous single-statement path, heterogeneous rawBulkUpdate CASE builder on SQLite/MariaDB/PostgreSQL, and the transactional per-row fallback) and the query-builder updateMany all merge the JSON data patch now — they used to write the patch as the entire blob for every matched row. Where the dialect operator is exact the merge stays inside the statement (zero reads, zero extra round trips); a nested-object or explicit-null patch on SQLite/MariaDB cannot be expressed in one SET, so the CASE builder refuses the fast path and the row-by-row path merges each row against its own stored blob.
    • Query-builder silent drop fixed: SqlQueryBuilder.updateMany dumped its payload straight into Drizzle .set(), which discards every field that is not a physical column — blob fields (e.g. title) never persisted through that path. It now routes through the same prepareValues contract as crud.update/batch.bulkUpdate.
    • MongoDB update parity: Mongoose strict (the schema default) dropped undeclared $set paths, so patching a dynamic field the collection schema did not declare was a silent no-op on MongoDB while SQL adapters stored it in data. All Mongo update sites (crud.update/updateMany, bulkWrite, both query builders) now pass strict: false; the namespace/field-permission layer remains the schema gate. Covered by a new cross-engine case that patches an undeclared field.
    • MongoDB query-builder resolution bug (found while testing): MongoDBAdapter.queryBuilder() resolved its model with the raw collection name while crud.* normalizes it — the lookup missed, and _getOrCreateModel built a generic model bound to a differently named MongoDB collection. Every builder operation (find/count/updateMany/deleteMany) silently ran against an empty collection. Now resolved exactly like MongoCrudModule._getRepo.
    • Verification: tests/integration/databases/bulk-operations-contract.test.ts (partial-patch merge, nested-patch merge, per-row blob fields, homogeneous blob patch, query-builder persistence) and the parity suite’s dynamic-field case — green on SQLite, PostgreSQL, MariaDB and MongoDB.
  • Engine-Native Dynamic-Field Filters: Containment + GIN on PostgreSQL (September 2026):

    • The gap: a field that is not materialized into a column lives in the JSON data blob, and an equality filter on it was translated on every SQL engine to a text extraction (data->>'field' = $1) — a form no index can serve. Measured 2026-09-22 on 100k rows (PostgreSQL): Seq Scan 7.9–10.9 ms per filtered read, growing linearly with the table.
    • The fix (engine-specific by construction): PostgreSQL now emits containment — data @> '{"field": value}'::jsonb (getJsonEquals, wired through mapQuery for $eq and $in, one probe per scalar value in an OR for lists ≤ 32) — and createModel provisions a jsonb_path_ops GIN index (<table>_data_gin) for every dynamic collection table. Measured after: Bitmap Index Scan 0.1–1.7 ms, and the cost stops growing with table size. Index build 347 ms once at provisioning; write cost measured within run-to-run noise (interleaved A/B/A rounds: −0.9 % inserts, +5.4 % updates). Write-only collections opt out with schema.jsonIndex === false.
    • Type fidelity: the probe serializes the RAW filter value, so a number filter probes a JSON number and a string filter a JSON string — {views: 101} matches a stored number but not a stored "101". SQLite (typed json_extract) and MongoDB (type-aware comparison) already behaved that way; the previous PostgreSQL text comparison did not, so this increases cross-engine parity. MariaDB stays the documented looser exception (JSON_UNQUOTE comparison coerces). Dotted paths probe nested objects (a.b → {"a":{"b":…}}), matching the data#>>'{a,b}' form.
    • Why the other engines keep the extraction form: SQLite and MariaDB have no single index that serves arbitrary JSON paths. Their native answer is already shipped — the row-store hybrid: declare the field indexed: true (or materialize: true) and it becomes a real column with a real index. The implementation notes now say so explicitly instead of leaving it implicit.
    • Verification: tests/integration/databases/dynamic-field-filter-index.test.ts — semantics (number/string/$in probes, type fidelity) on all four engines, the engine-native SQL shape per dialect, and on PostgreSQL an EXPLAIN (ANALYZE) of the adapter’s own rendered filter SQL asserting the GIN index is used and no Seq Scan appears (bulk-seeded table with real statistics, isolated from the row probes). Full sweep: SQLite 49/49 · PostgreSQL 51/51 · MariaDB 49/49 · MongoDB 48/48 across the parity, bulk-contract, mutation and dynamic-filter suites.
  • System-Schema Fingerprint + Legacy Tails Removed — Boot No Longer Touches an Unchanged Schema (September 2026):

    • Measured before: a provisioned PostgreSQL paid 124 ms per boot re-running all 118 CREATE TABLE/INDEX IF NOT EXISTS statements (0.4–1 ms per round trip), plus ~35 ALTER TABLE … ADD COLUMN IF NOT EXISTS repair statements (~60 ms; each one costs 1.7–5 ms versus 0.96 ms for a trivial SELECT).
    • Fingerprint: svelty_schema_state stores a sha-256 over SYSTEM_SCHEMA per dialect; a match skips the spec-derived pass. Any spec change (new table, changed type/default, new index) changes the fingerprint and re-runs the pass, so it is drift-free by construction rather than a hand-bumped version; a pass with a failed statement does not store the marker, so a half-applied schema is retried instead of trusted, and any marker error degrades to “run the pass”.
    • Legacy tails deleted: the per-engine repair tails are gone — the spec is the only schema trail. Audited first: every column they added (auth_users.isDeleted/deletedAt, auth_api_keys.rateLimit/status/lastUsed/keyHash) was dead legacy (the current model uses lastUsedAt/lastUsedIp/revoked, and the users table never soft-deleted), and the one substantive repair (MariaDB mfaVerifiedAt → DATETIME(3)) is already declared in the spec, so fresh installs keep millisecond MFA precision. The v0.0.8-era security → password and redirects_mv.from|to renames are dropped with them; the compatibility consequence is documented in Upgrading SveltyCMS.
    • Measured after (fresh database → provisioned by the spec alone): 327 ms once, creating 27 tables + 117 indexes; every later boot 2.1 ms (skipped: true) — ~122 ms saved per start on PostgreSQL. Verified through the compiled build on fresh databases for SQLite, PostgreSQL and MariaDB (all boot with Bootstrapping system schema... then System schema up to date (<fp>) — skipped the DDL pass on the next boot), with the adapter-parity suite green on each.
  • Cross-Engine crud.aggregate() — the MongoDB Pipeline Runs on SQL (September 2026):

    • The gap: aggregate() was MongoDB-only, so SQLite/PostgreSQL/MariaDB answered every pipeline with NOT_SUPPORTED and a generic caller could not express a grouped count without raw SQL. The capability flags admitted it (supportsAggregation: false).
    • The translation (core/aggregation-translator.ts, pure and unit-testable): $match → WHERE through the adapter’s own query mapper, so JSON fields, containment and the tenant scope behave exactly like find*; $group → GROUP BY with $sum / $avg / $min / $max / $count and a scalar _id (dotted paths allowed, $sum: 1 → COUNT(*)); $sort / $skip / $limit → ORDER BY / OFFSET / LIMIT; $count and include-only $project → the select list. Without a group/$project the result is the findMany document shape, which is what MongoDB’s aggregate returns too.
    • Fail-closed by design: $lookup, $unwind, $facet, a compound _id, out-of-order stages and a $match that maps to no condition return a named NOT_SUPPORTED message that names the workaround — never a silently wrong number. The refusals are returned outside wrap(), which would otherwise nest the envelope into { success: true, data: { success: false, … } } and make a refusal read as a success (caught by the new contract test, not by the type system).
    • $min/$max are the one engine-dependent stage, and they now say so: PostgreSQL and MariaDB extract JSON as TEXT, so MIN over {10, 2} returns "10" (measured on both; PostgreSQL additionally defines no min(jsonb)), while SQLite’s typed json_extract and MongoDB compare natively. A blob field therefore gets a refusal naming the fix — declare it indexed / materialize: true — instead of a lexicographic answer; a materialized column aggregates natively on every engine (new dialect hook getJsonOrderedField).
    • PostgreSQL numeric range filters fixed on the way: views >= 4 compared TEXT ('16' < '4', so stored 16/32 were missed), and the naive JSONB replacement was worse in the other direction — jsonb orders String below Number, so an unguarded < admitted a stored "101" into views < 4 (verified: '"101"'::jsonb < '4'::jsonb is true). Ranges now read jsonb_typeof(data->'views') = 'number' AND data->'views' >= '4'::jsonb, type-strict in both directions and identical to SQLite/MongoDB behaviour.
    • SQLite dotted-path parity: json_extract(data, '$."meta.lang"') treated the dotted name as one literal key, so a {"meta.lang": "en"} filter matched nothing there while PostgreSQL (data#>>'{meta,lang}') and MariaDB ($.meta.lang) resolved the nested value. Segments are quoted individually now ($."meta"."lang").
    • Capability flags and SDK surface: supportsAggregation is true on SQLite/PostgreSQL/MariaDB — the documented subset, with unsupported stages still failing closed, which the pre-existing contract test now asserts (3 × active / 2 × inactive instead of an envelope check). CollectionModel.aggregate on MongoDB routes through crud.aggregate (tenant $match injection, normalized collection resolution, logged failures) instead of the raw model.aggregate, and both SDK wrappers log a refused or erroring aggregate instead of silently returning [].
    • Verification: tests/unit/databases/aggregation-translator.test.ts (20 cases — plan shapes, accumulator mapping, purity, every refusal) and tests/integration/databases/aggregation-contract.test.ts (12 cases — group/match/count/sort/limit/project, tenant scoping without an explicit $match, materialized $min/$max, the engine-dependent $min refusal, NOT_SUPPORTED parity), green on SQLite 12/12 · PostgreSQL 12/12 · MariaDB 12/12 · MongoDB 9 + 3 intentional skips; the same-commit sweep of the parity, bulk-contract, dynamic-filter and advanced-CRUD suites is green on all four engines.
  • Competitive lanes: read payload de-duplicated, write ack made adapter-agnostic (September 2026):

    • The defect: every SQL read merged the JSON data blob into the document root and also serialized the blob as a nested field. The external harness counted 3,339 B for a 1,558 B document (2.14x) — “~200 B system columns + data{…1558} + the same 8 content fields flattened again” — and it was the only excess in that comparison that was pure duplication rather than envelope convention (Keystone 1.00x, Strapi 1.03x, Wagtail 1.09x).
    • The fix: flattening now consumes the blob — the key is dropped by key, not identity, because normalizeJsonFieldValue PARSES a TEXT blob (SQLite/MariaDB) into a different object, which is exactly why an identity check silently kept it on the wire — and each of the three call sites stopped copying the raw blob (or its TEXT form) back onto the row. A collection field literally named data is merged and survives; a legacy string-encoded blob still resolves through the JSON layers.
    • Measured end-to-end (the payload-budget integration case logs it): point read 3,339 B → 1,851 B (1.23x a 1.5 KB document), PATCH 3,519 B → 1,814 B — i.e. document + the ~200 B of real system columns, no second copy.
    • Prefer: return=minimal (RFC 7240) on PATCH/PUT answers {success, data:{_id}} — 70 B measured — on both the warm-session write lane and the API dispatcher. Absent or unknown values keep the full representation, so no existing client changes behaviour.
    • Adapter-agnostic write ack: the preference maps onto the existing BaseQueryOptions.skipReturning contract instead of being a response-shape trick, so the row read-back is skipped too — SQL engines omit RETURNING and reconstruct the ack from the prepared payload, and MongoDB now honours the flag as well (it called findOneAndUpdate unconditionally, i.e. the no-read-back path was silently SQL-only). Pinned by the new write ack (skipReturning) parity case: SQLite 24/24 · PostgreSQL 24/24 · MariaDB 24/24 · MongoDB 23/23.
    • Point-tier admission filter: admitting a point read on its first touch meant a 100k-uniform-random scan filled the 2,000-slot L1 point tier with entries that were evicted before ever being read — putEntry + byte accounting + indexKey + FIFO sweep + a second encoded buffer per cold read, for zero hits. An id now takes a slot on its second sighting (bounded FIFO filter): a cold scan costs one Set insert and no response slot, while a repeated id is cached from its second request on.
    • Verification: unit 4,855/4,855; SQLite integration 685/685; tests/unit/databases/read-payload-shape.test.ts (payload shape, the data-named-field case, the legacy string form), tests/unit/utils/http-preferences.test.ts (header parsing incl. last-wins), tests/unit/benchmarks/document-integrity.test.ts (the benchmark guard that found the upsert loss) and the payload-budget case in tests/integration/api/collections-mutations.test.ts.

3.26 September 2026 — Fast lanes on by default, gated by system state

The raw-transport fast lanes answered a narrow request class (warm-session admin collection reads) straight to the socket, skipping adapter-node’s IncomingMessage → Request and Response → stream bridging while calling the very same lane function the pipeline calls. They shipped opt-in (SVELTY_FAST_LANE=1, default off) with two open questions: the bypass point sits before every hook, and no lane re-applied the system-state decision.

  • system-state is now re-implemented for lane traffic (src/hooks/lane-state-gate.ts), against the same module-level state mirror the gatekeeper reads, with the same predicate as handleTurboPipeline: READY/WARMED/WARMING/DEGRADED serve, SETUP/IDLE/INITIALIZING/MAINTENANCE/RECOVERY/FAILED decline to the pipeline. Consulted in the read lane, the write lane and the registry dispatch — the last one so a lane added later cannot serve a state the operator took the instance out of.
  • Default flipped to ON (SVELTY_FAST_LANE=0 opts out) so the setting is a documented deployment default rather than a benchmark-only flag, and the transport is symmetric rather than a comparison artifact.
  • Regression caught by measurement: the lane shim converted the prebuilt body with Response.text() and handed the string to res.end(), so every wide list body was decoded and re-encoded per request — listLarge TURBO-HIT 2.64 ms bridged → 4.76 ms through the lane. Handing Node the bytes (arrayBuffer(), content-length from byteLength) restores 2.85 ms.
  • Measured, paired (PostgreSQL, 10 000 docs, 8c, replica protocol, 3–4 rounds per arm): findByIdRandom 3,235 → 3,655 RPS median (+13 %) with the lane on versus off; findById 7,067 → 9,194; listPlain 7,187 → 10,876; findMissing 8,138 → 9,496. A dedicated 20,000-read load driver agrees: 3,080 → 4,401 RPS, with the server-only CPU profile putting ~10 % of samples in undici Request construction, 5.7 % in adapter-node’s static-asset check and ~12 % in socket writes.
  • Confirmed at the published dataset size (100,000 docs, paired, update carried as the control lane because it is not lane-served): findByIdRandom 2,631 → 3,919 (+49 %) and 3,139 → 3,883 (+24 %), median 2,885 → 3,901 RPS (+35 %) — +11 % over the published competitor row (3,504 RPS) — while the control stayed flat at ±3 %. This supersedes an earlier session’s reading that the row goes neutral or negative at 100 k; that reading predated the byte hand-off fix and was taken with the lane still opt-in.
  • Write coalescing was built, gated, and rejected by its own gate (SVELTY_WRITE_COALESCE, since deleted) — see the roadmap entry: 120/128 concurrent PATCHes batched correctly at an average size of 5 and every caller got its own row, but the lane measured −29 % median over three alternated pairs on PostgreSQL (median 1,305 → 933 RPS, warm p95 7.7 → 15.7–20.4 ms) against a flat control, because at 8 workers PostgreSQL already group-commits overlapping statements. The negative result is recorded rather than shipped as an off-by-default path.
  • Verification: unit 4,866/4,866; tests/unit/hooks/fast-lane-policy.test.ts pins the state matrix, the dispatch backstop and the default-on/opt-out contract; collection-read-lane.test.ts establishes its operational-state precondition explicitly.

3.27 September 2026 — WebMCP Agentic Builder, Native Forms, Layout-Aware Responsive Images, Multi-Breakpoint Canvas & Atomic SSG

To lead visual and headless CMS platforms (such as Instatic) through 2026/2027, six architectural pillars were implemented and verified with zero-overhead principles:

  • WebMCP Builder Tools (src/plugins/webmcp/tools/builder.ts):
    • Standardized Model Context Protocol tool endpoints (design_collection, refine_collection, diff_collection_schema, scaffold_widget, list_available_widgets) allowing Cursor, Claude Code, and external AI agents to author and evolve schemas directly over standard WebMCP protocol.
    • Draft-by-Default Airgap: AI proposals generate structured AST diffs and validated draft schemas rather than mutating live production tables, preventing untracked schema drifts.
    • Integrated with fetchApi including CSRF token headers and session credentials.
  • Native Zero-Config Form Ingestion (src/routes/api/[...path]/handlers/forms.ts):
    • Direct headless/static site form ingestion at POST /api/forms/:collection.
    • Built-in honeypot spam protection (_hp, _gotcha) without third-party recaptcha tracking.
    • GDPR-compliant, privacy-first SHA-256 IP salt hashing (_formIpHash).
    • Publishes events to internal EventBus (form:submit) enabling zero-latency automations and notifications.
  • Layout-Aware Responsive Images (src/components/ui/image.svelte):
    • Native layout presets (full, half, third, quarter) calculating mathematical, layout-aware sizes strings across standard viewport breakpoints.
    • Zero-CLS placeholder blur styling utilizing ThumbHash/LQIP hashes to eliminate layout shifts during hydration.
  • Multi-Breakpoint Split Canvas (src/plugins/editable-website/live-preview.svelte):
    • Live side-by-side synchronized rendering of Desktop (fluid min-w-[580px]) and Mobile (375px) viewports.
    • Real-time two-way postMessage synchronization for click-to-edit and field highlight.
  • Atomic Static Site Generator (src/services/site/static-publisher.server.ts):
    • Pre-renders published pages into a sibling staging directory (<target>.staging-<uuid>) so the final swap is a same-filesystem, atomic rename (never EXDEV).
    • Moves the previous build aside and restores it if the swap fails — the live directory is never deleted before its replacement exists.
    • dryRun preview mode renders the route manifest (path + byte length per route) without touching the live site — the “preview before it goes live” step.
    • Schema-aware hardening: text fields HTML-escaped, authored body run through the server-side sanitizeHtml, and slugs reduced to traversal-proof [A-Za-z0-9_-] segments.
    • Exposed via POST /api/export with { "type": "static-site", "preview": true|false }.
  • Hue-Preserving OKLCH Palette Ramp (src/utils/color-ramp.ts) — inspired by Instatic’s Core Framework token engine:
    • One brand hex expands to a full, perceptually even 50–950 ladder: the seed lands verbatim on its anchor step (500 for accents, 50 for surfaces), hue is preserved, and chroma tapers toward the extremes.
    • Out-of-gamut steps are mapped by reducing chroma, never by clipping RGB (clipping would shift the hue).
    • Replaces the previous 3-step color-mix shorthand expansion in @utils/theme-preset-mapper, so accent roles now get the same complete ladder as surfaces; non-hex input keeps the old color-mix path.
  • Verification:
    • Unit tests: tests/unit/plugins/webmcp-builder.test.ts (4/4), tests/unit/api/forms-handler.test.ts (4/4), tests/unit/site/static-publisher.test.ts (5/5), tests/unit/color-ramp.test.ts (7/7) passing in <500ms.
    • All security and risk audits (risk-audit.ts: 5/5) clean.

3.29 September 2026 — 2027 Two-Plane Core: Wire Plane Admission, Compiled Point Reads & Dynamic Bitsets

Separated read operations into an opt-in compiled Wire Plane and an enterprise Domain Plane, establishing the foundation for sub-millisecond edge queries with full fallback safety:

  • Strict Wire Plane Admission & Compiled Point Reads (isWirePlaneAdmissible + computeCollectionWireMeta in src/hooks/handle-collection-read-lane.ts; findPointWireStream on the adapters) — Only simple, published single-entry reads with compiled default-locale alignment were admitted to the wire path; admitted reads ran as cached point statements emitting engine-side JSON (json_object / jsonb_build_object / JSON_OBJECT) on the SQL adapters, and everything else fell through to the Domain Plane.
  • Fail-Closed Wire Admission Predicate (isWirePlaneAdmissible in src/hooks/handle-collection-read-lane.ts) — Rigorous admission gate guarding Direct-to-Wire SQL streams: checks published status (rejects drafts, review status, and preview cookies), prohibits dynamic relational expansions (populate), validates compiled default locale alignment, enforces projection equivalence (fields), and verifies absence of mutating afterRead hooks.
  • Multidimensional Dynamic Bitsets (src/databases/auth/permission-bitmask.ts) — Augmented permission evaluation with multidimensional dynamic bitset chunks (permBits: number[], permRev: number) cached in sessions (SessionManager), enabling $O(1)$ in-register authorization checks for hundreds of permissions beyond 64 bits.
  • LRU-Bounded Tenant Pools (src/databases/postgresql/adapter-core.ts) — Introduced connection pool slice bounding (MAX_TENANT_POOLS, default 16) for dedicated multi-tenant DSNs to prevent socket and memory exhaustion under high tenant density.
  • The 4-Cell Benchmark Contract (now documented with the benchmarks: Performance Architecture) — Codified performance classification rules distinguishing Cell 1 (in-memory L1 cache hits), Cell 2 (Domain Plane miss), Cell 3 (Wire Plane direct SQL miss), and Cell 4 (driver hardware limits), preventing cache-hit category errors.
  • Verification:
    • tests/unit/hooks/collection-read-lane.test.ts (16 tests passing) validating all admission predicate constraints.
    • tests/unit/auth/sessions/session-multidimensional-bitset.test.ts verifying dynamic bitset encoding and session persistence.
    • tests/unit/multi-tenancy/physical-pool-partitioning.test.ts verifying tenant pool slice bounding.

The raw fast-lane transport only carried warm-session collection reads; writes stayed on adapter-node’s bridged path because they need the request body. The write lane now has a transport twin, opt-in (SVELTY_FAST_LANE_WRITE=1, default off) until an equivalence run:

  • One implementation, not two — collectionWriteLane calls the same tryCollectionWriteLane the pipeline calls, so WAF path inspect, CSRF same-origin/double-submit, rate limiting, warm-session admin/RBAC, per-request tenant resolution and the security-header contract are all reused unchanged.
  • Never consume a body you cannot answer — the lane refactor passes the already-resolved turbo context into serveWarmCollectionWrite, which now guarantees a Response after commit (no TTL race), and the entry exposes the body lazily (readBody()): a lane that declines (cold session, non-simple path, maintenance state) returns to the pipeline with the stream unread. A hard body cap (256 KiB default, SVELTY_FAST_LANE_MAX_BODY) rejects oversized payloads before they are buffered.
  • Rate-limit parity by construction — the entry resolves the client address with adapter-node’s exact ADDRESS_HEADER/XFF_DEPTH rules and reproduces its failure mode (undefined → throw → getClientIp fails closed to 0.0.0.0), so lane and pipeline bucket identically; Set-Cookie emitted inside the lane is carried on the result.
  • Verification — tests/unit/hooks/fast-lane-write-policy.test.ts pins the four invariants: write lane unregistered without the opt-in, declines without consuming the body, reads the body exactly once when committed, and fails closed on the client address.

References:

3.30 September 2026 — Wire Plane visibility parity, lane gates & single-transport lean-out

A security review of the 2027 Two-Plane work found that the fast read lane had drifted from the pipeline it mirrors. The lane builds its own Response, so every check that lives in a skipped hook had to be re-established deliberately — three had not been.

1. Publication clamp bypassed on the wire path. The compiled point statement had no status predicate, so a session that the Domain Plane clamps to published could fetch a draft by id. The wire contract now carries requirePublished:

  • PostgreSQL (jsonb_build_object), SQLite (json_object), MariaDB (JSON_OBJECT) compile AND "status" = 'publish' into the cached statement (both the plain and tenant variants).
  • MongoDB adds status: { $in: ["publish", "published"] } to the lean query.
  • Where a table has no status column the adapter fails closed (null), and the base-class fallback re-checks the row object before serializing, so an unfiltered fallback read can never emit unpublished bytes.
  • The read lane passes the flag only when resolvePublicationFilter says the caller is clamped — the same function the Domain Plane uses, so the two cannot disagree. Admins keep the unguarded (faster) statement for drafts, and a draft returns null → Domain Plane decides.

2. The clamp never matched anything. loadOneById compared the filter name (published) against the stored value (publish), which denied every status-bearing row to a clamped caller and would have admitted any row stored as published. It now uses the shared isPublishedStatus() predicate (accepts both spellings) and leaves status-less rows untouched, preserving the previous behaviour for non-status-versioned tables.

3. Read-lane gates re-established (all evaluated before any cache lookup, so a stored body can never outlive a policy change):

Gate Before After
Endpoint parity collection:read or collections:read collections:read only — exactly what ENDPOINT_PERMISSIONS requires for /api/collections GET
FLAC Not evaluated: the lane never reaches handleTokenResolution, the read path’s only applyFieldPermissionsToBody call site isLaneFlacExempt() declines when a FIELD_PERMISSIONS policy or a readRoles-guarded field applies; fails closed on error
Grant freshness turbo.permMask served until the sliding TTL expired isPermissionBitsetStale(turbo.permRev) declines on epoch change; setTurboAuthContext stamps the epoch, so invalidateRoleBitsetsGlobally() (already called by setRolePermissions/invalidatePermissionCache) now reaches lane traffic

4. Single transport (superseded 3.29’s dispatcher). src/hooks/kernel-dispatcher.server.ts is deleted, together with its registration and 11 unit tests. It answered the same requests as tryCollectionReadLane with its own ETag/cache/304 semantics, built a real new Request(...) + new URL(...) + full header reduction per request (the bridging cost the lane exists to avoid), carried BASE_HEADERS without applyAllSecurityHeaders’ HSTS/CORS/Vary work, and stamped a transport-specific x-kernel-channel header. LANE_BYPASSED_HOOKS and the guard test keep the bypass policy explicit for the remaining lanes.

5. Dead code removed (verified zero importers, no glob/dynamic-import hit): src/components/command-bar.svelte and search-component.svelte (self-described backward-compat aliases for command-palette.svelte), src/databases/mongodb/index.ts (dead barrel), src/services/core/widget-discovery.ts (superseded by widgets/scanner.ts), src/services/database/index-optimizer.server.ts, src/services/site/website-starter-test-seed.server.ts (re-export shim) and the unified-data-hub/server/federation-enrichment-utils.ts shim (its one test consumer now imports the real module). The unreferenced watermark trio was removed in the same pass and reinstated the next day — together with the rest of the native UI kit — under the maintainer’s rule that unused-but-reusable components stay in-tree because Vite tree-shakes them out of the bundle (see 3.31).

6. Test-suite lean-out. The largest per-file cost in tests/unit was @src/paraglide/messages — a barrel over 1 462 generated modules (6.4 MB) that every widget test paid in transform time. Mocking that boundary in the widget suites plus de-duplicating fixtures and replacing near-vacuous assertions (expect(schema).toBeDefined(), formula-against-itself tests) cut measured wall time substantially with more real assertions than before: widgets 21.90 s → 3.07 s, media 1.73 s → 0.86 s, utils 4.62 s → 3.52 s (combined 128 files / 1 082 tests in 4.53 s). Correction (re-verified 2026-09-27 by a second agent): vitest.config.ts’s src/widgets/custom/**/tests/**/*.test.ts glob is live — 16 co-located suites exist and run, in addition to near-duplicate copies under tests/unit/widgets/custom/ (measured drift: 3 lines for address, 165 for seo; 72 tests / ~5 s per unit run executed twice). Consolidating them is a coverage-ownership decision and was completed on 2026-09-27 — the co-located suites are now the single home and the duplicates are gone (see 3.32). TokenPicker remains unwired, not dead — fields.svelte keeps the openTokenPicker() handler (the component itself is never mounted) and it is documented in docs/reference/components/fields.mdx.

Verification: bun run check (format + lint + dead-class + design-token + shade contract + lockfile) green, bun run lint 0 warnings / 0 errors, bun run test:unit 4 962 passed. The new tests/integration/databases/differential-parity.test.ts case asserts, per adapter, that a draft is invisible and a published row visible under requirePublished, and that the unguarded read keeps agreeing with findOne.

3.31 September 2026 — Diagnostics to zero, navigation cancellation, lean-out batch 2 & bench-ledger verification

1. Diagnostics: 42 errors → 0 (warnings → 0). The root cause of most of them was a type contract that the codebase was violating in four places, not four separate bugs:

  • DatabaseResult<T> requires error on the failure branch, but sql-adapter-core (×2) and the MongoDB wire catches (×2) returned { success: false, message } / { …, data: null }. The producers now emit error: createDatabaseError(…), which fixes the union for every caller.
  • The wire tests read result.data without narrowing (TS does not narrow on expect(x).toBe(true)). New tests/integration/databases/wire-contract.ts provides requireWireMethods() (fails loudly when an adapter lacks the Direct-to-Wire contract — no silent skip) and unwrapResult(), so all 21 + 14 errors in the two parity suites collapse into two typed calls.
  • src/widgets/core/input/index.ts typed its schema param as ReturnType<typeof InputWidget> while InputWidget referenced the schema — both sides inferred each other into any (TS7022/7023). The param is now InputProps & { required?: boolean; translated?: boolean }, matching the media-upload widget’s convention.
  • mariadb/adapter-core.ts returned mysql2’s QueryResult union from getRawExec (typed any[]); narrowed at the boundary.

2. A documented race guard that cancelled nothing. NavigationManager.executeNavigation created an AbortController, aborted the previous one — and never read the signal, so a stale run still mutated stores and pushed its own goto(), and its finally cleared the newer run’s loading state. The task signature is now (signal: AbortSignal) => Promise<void>; toList/toEdit/toCreate bail before mutating and before goto, and only the newest navigation owns the loading entry.

3. Lean-out batch 2 — only two modules actually left the tree. The batch started from 24 candidates (each re-checked for zero importers and zero documentation references), and the maintainer’s rule that unused-but-reusable native UI must stay — Vite tree-shakes unreferenced modules, so an unmounted component costs nothing in the bundle while remaining available to the front-end — was applied to all but these two, which are genuine duplicates of live code:

  • src/databases/sqlite/sqlite-dialect.ts + its IDialectProvider interface — duplicate of SQLiteAdapterCore.applyPragmas(), which is hardware-tiered and called on every connect.
  • src/plugins/cookie-consent/api.server.ts — unreachable (registered nowhere). Reinstated immediately afterwards because it is the only implementation of the documented server-side consent logging; wiring or dropping that feature is now a roadmap item.

Everything else was restored and is intentionally unreferenced: collection-builder-schema-viewer, global-loading, service-worker-registration, system/{author-brief,background-pattern,restart-required-banner,widget-validation-warning}, plugins/page-speed-score, ui/{index.ts barrel,date-picker,marquee,radio-group,radio-item,segmented-control}, rich-text/components/{color-selector,drop-down,image-description}, seo/components/{analysis-modal,meta-input,robots-meta-input}, collectionbuilder/nested-content/modal-name-conflict, utils/schema-impact (+ its test), and the watermark trio. A second agent verified all 122 files in those sets resolve every import (0 failures) and measured their importer counts (0 each, except the watermark children which only the watermark editor imports).

Two audit claims were checked and rejected: services/ai-builder/index.ts is live (the API handler dynamically imports the barrel in three places) and TokenPicker, virtual-media-grid, transcode-modal, two-factor-setup-modal, modal-schema-warning and VideoDialog are all documented — deleting them is a product decision, not cleanup (see the roadmap).

4. Bench ledger verified, and the reporter was lying about direction. BENCHMARK_RECORD=1 … --db=sqlite --only=truth-latency (twice, same code) against the recorded 2026-09-26 baseline:

Truth-latency row Baseline Run A Run B
HTTP end-to-end (warm) 0.911 ms 0.486 ms 0.393 ms
HTTP end-to-end (warm p95) 1.122 ms 0.620 ms 0.481 ms
HTTP end-to-end RPS 1 093 2 042 2 527
HTTP cold, cache-busted (warm) 1.290 ms 0.508 ms 0.458 ms
HTTP cold, cache-busted RPS 772 1 957 2 171

The improvement is ~2× and exceeds the run-to-run spread (the two same-code runs differ by up to 24 %). Attribution caveat, stated plainly: the plausible cause is the single-transport lean-out (§3.30) — one fewer lane on the point-read path — while rows like Local SDK (Full) also move with warm-cache/JIT state inside a run. A defensible attribution needs the documented interleaved A/B (base ref → run → current ref → run), which is queued, not claimed.

The reporter itself had a sign bug: writeRankedExecutiveSummary sorted by |delta| and titled every entry 🔴 Top Regressions, so improvements were printed as regressions (truth-latency: -98% with a red dot). It now splits Top Regressions (positive delta only, with an explicit “none” line) from Top Improvements, which is why the ledger now reads correctly.

3.32 September 2026 — Two-Plane read-lane hardening, six product-bug fixes & a zero-warning tree

The read lane builds its own Response, so every check that lives in a hook it skips has to be re-established deliberately. This wave closed that gap, removed the second transport, and took the project’s own diagnostics to zero.

1. Publication clamp on the wire path (all four engines). requirePublished compiles AND "status" = 'publish' into the cached point statement on PostgreSQL (jsonb_build_object), SQLite (json_object) and MariaDB (JSON_OBJECT), and $in ["publish", "published"] on MongoDB. A table without a status column fails closed, and the base-class fallback re-checks the row object before serialising. The clamp itself never matched anything: loadOneById compared the filter name (published) against the stored value (publish), denying every status-bearing row to a clamped caller while an accidental published row would have slipped through; it now uses the shared isPublishedStatus() predicate. Verified per adapter by tests/integration/databases/differential-parity.test.ts (draft invisible, published visible, unguarded read still agreeing with findOne).

2. Three lane gates re-established (evaluated before any cache lookup, so a stored body cannot outlive a policy change): endpoint parity (collections:read only — what ENDPOINT_PERMISSIONS requires), isLaneFlacExempt() (declines when a FIELD_PERMISSIONS policy or a readRoles-guarded field applies; fails closed on error), and isPermissionBitsetStale(permRev) (a role edit now reaches lane traffic instead of serving the old grant set for the whole sliding TTL).

3. One transport. src/hooks/kernel-dispatcher.server.ts is deleted with its registration and 11 unit tests: it answered the same requests as the collection read lane with its own ETag/304 semantics and built a real new Request(...) + new URL(...) per request — the bridging cost the lane exists to avoid.

4. Diagnostics 42 → 0 by fixing producers, not assertions. DatabaseResult<T> requires error on the failure branch; the SQL wire catches and the MongoDB wire catches returned { success: false, message } instead, which broke the union for every caller. New tests/integration/databases/wire-contract.ts adds requireWireMethods() (fails loudly when an adapter lacks the Direct-to-Wire contract — no silent skip) and unwrapResult(), collapsing 35 untyped accesses in the two parity suites; the input widget’s self-referential schema types became an explicit InputProps & { required?: boolean; translated?: boolean }; the MariaDB getRawExec result union is narrowed at its boundary.

5. Widget suites consolidated — 16 files / 72 tests removed with no lost coverage. The near-duplicate suites under tests/unit/widgets/custom/ were ported into the co-located src/widgets/custom/*/tests/ homes (the documented portable intent, and the glob vitest.config.ts already runs). The port was mechanical only after a per-pair check that the modern suite covers every legacy case name and has an equal-or-higher assertion count; seo was the single pair whose table-driven rewrite defeated the name check, so its nine cases were verified by hand before the duplicate was removed. The namespace-ownership guard test then caught a registry that still pointed at the removed paths — the kind of stale reference the guard exists for. Net effect: 551 files / 4 891 tests in 17.9 s (was 567 / 4 963).

6. A documented opt-out that could never apply. PostgreSQL’s createModel created the jsonb_path_ops GIN index behind (schema as { jsonIndex?: boolean })?.jsonIndex !== false, where schema resolved to the schema module namespace rather than the collection definition passed in as schemaData — so the guard was statically always-true (rolldown flags it as IMPORT_IS_UNDEFINED) and jsonIndex: false was unreachable. It now reads the collection config, making the documented write-only opt-out real and removing the build warning.

7. Benchmark ledger and a reporter that inverted direction. With BENCHMARK_RECORD=1 … --db=sqlite --only=truth-latency (twice, same code) against the recorded 2026-09-26 baseline:

Truth-latency row Baseline Run A Run B
HTTP end-to-end (warm) 0.911 ms 0.486 ms 0.393 ms
HTTP end-to-end (warm p95) 1.122 ms 0.620 ms 0.481 ms
HTTP end-to-end RPS 1 093 2 042 2 527
HTTP cold, cache-busted (warm) 1.290 ms 0.508 ms 0.458 ms
HTTP cold, cache-busted RPS 772 1 957 2 171

The improvement exceeds the run-to-run spread (two runs of identical code differ by up to 24 % here), but attribution still needs the documented interleaved A/B (base → run → current → run), which is queued rather than claimed. The reporter’s ranked summary sorted by absolute delta and titled every entry 🔴 Top Regressions, so improvements were printed as regressions; it now splits Top Regressions (positive deltas only, with an explicit “none”) from Top Improvements.

8. E2E: two reproducible failures, traced to real defects. A full local run’s failure set is disjoint run-to-run and drains under isolation (the contention class tracked in the test-status dashboard), but two specs reproduced in solo, clean-database runs — and tracing them found product bugs in src/, not host state: a $effect on /config/design-system that reverted every local edit before saving, and avatar writers that left the layout snapshot stale so the delete button never rendered (both in 9). The spec that documented the first also needed an outcome-verified selectOption helper — a selection can land before hydration attaches the binding, in which case the DOM snaps back to its server-rendered value, which is how the revert was first observed. The same outcome-verified retry (input → assert the outcome, never a longer sleep) now guards the two other interactions that a hydration gap could drop: the Edit User Details dialog and the prep-phase sign-in submit that had been aborting whole E2E runs.

9. Six product bugs — two surfaced by the type checker, two by E2E, two by the follow-up trace — all fixed.

  • Uploads scoped to a phantom tenant. media-upload/process-upload.ts calls saveMedia(file, userId, access, tenantId, basePath); both call sites passed the resolved folder path in the tenantId slot and never passed _basePath, so an entry upload was persisted under a folder-shaped tenant ("collections/pages") with no folder. The fourth argument is now ctx.tenantId and the fifth folderPath (the folder the widget already computed). Silent because nothing type-checked the call.
  • Marketplace install/license could never succeed. marketplace-view.svelte treated fetchApi’s envelope ({ success, data, message, code }) as a Response — res.json(), res.ok, res.status. Those are undefined on the envelope, so the handler threw a TypeError into its own catch and reported “Install failed” for every attempt. It now reads the envelope (res.success, res.data ?? res, message/code for the toast) and keeps the previous tolerance for payloads that arrive at the top level rather than under data.
  • A settings form that reverted every edit. On /config/design-system the overrides $effect read the same local state it wrote, so a local edit re-triggered it and was instantly pinned back to the persisted value (and then saved back). The writes are now scoped with untrack — the effect depends only on server prefs — which is the documented Svelte 5 shape for “sync from server data”. E2E design-system.spec.ts:68 reproduced the revert deterministically; a stale-density comment in the spec’s own finally block was a second symptom (it silently re-saved the old value).
  • Avatar writes left the layout snapshot stale for 15 s. getFreshLayoutUser() (layout-caches.server.ts) feeds the (app) layout (sidebar avatar, display name) from a 15 s L1 entry, invalidated only by the PUT /api/user/update-user-attributes route. LocalCMS.auth.saveAvatar / deleteAvatar write through the adapter directly, so an upload persisted correctly while the modal’s refreshed page data still said avatar: null and the delete button never rendered (E2E profile.spec.ts:174). Both writers — and the namespace’s updateUserAttributes — now drop the entry through one private helper.
  • Every server-rendered id in the admin UI drifted from its hydrated twin. generateId() (the native UI kit’s id source: input, select, checkbox, textarea, toggle, radio-group, tags, combobox, date-picker) falls back to a process-global counter unless initIdGenerator() has installed its per-render context — and the app never called it (only the SSR test wrappers did). Server ids therefore advanced with every request (select-2), hydration restarted at zero (select-0), and every label[for] / aria-describedby / aria-controls pointing at a generated id broke once hydration finished. The root layout now initializes the generator, and tests/unit/routes/layout-id-generator.test.ts fails if that call ever disappears.
  • Invalidation could lose a race with the next read. invalidateLayoutUserCache() was void cacheService.delete(…).catch(() => {}) — it returned before the delete landed, so the save→reload round-trip that the E2E exercises could still be answered from the old snapshot. It is awaited now (the module’s own header already promised “invalidate on user-attribute writes”), and Auth.updateUserAttributes — the facade every internal writer (roles, 2FA, admin edits) lands in — invalidates as well, so the HTTP route, the SDK namespace and the facade all honour the contract. The remaining direct-adapter writers (two-factor-auth.ts, magic-link.ts) touch authentication metadata only (twoFactorPending, backupCodes, lastAuthMethod, lastActiveAt), none of which the layout snapshot renders.

10. Type drift to zero. svelte-check reported 16 errors in 7 files and nothing in the gate noticed (bun run check = format + lint + dead-class + design-token + shade + lockfile; no tsc). Beyond the two bugs above: SessionResolution’s ok-arm was missing permMask (added, returned since the bitset work), collections.svelte computed a matchesFav that no branch used, bulk-field-edit-modal.svelte bound number | null into an input that accepts string | number | undefined (now undefined), the write-pipeline test fixture was typed as a full Schema where the helpers take a structural PrepFieldSchema, and the benchmark’s optional findPointWireStream is narrowed through requireWireMethods() instead of a cast. All 16 are fixed; wiring svelte-check into the gate is a roadmap item.

11. Benchmark harness: two defects and a reporting bug. etag-hash asserted that the pure-JS FNV-1a loop beats native SHA-256 at 547.8 KB; measured 818 µs vs 215 µs (SHA-256 3.8× faster above the cache boundary), while the loop does win for the sizes fastHash actually hashes (collection/widget sources, largest measured 23 KB). The assertion now splits at the measured crossover (strict superiority ≤16 KB, a µs/KB scaling guard above) instead of deleting the check. dev-dependency-load crashed its own child: the matrix exports NODE_OPTIONS=--max-old-space-size=1024 to children, and under that 1 GB cap svelte-check aborts with a V8 fatal error (exit 134, node::OnFatalError, ~12 s in), which the probe read as “Type Check failed” — reproduced by setting the variable alone, clean without it. The DX probes now spawn with NODE_OPTIONS stripped. Finally, reporting.ts formatted the per-adapter ledgers with an unref()d oxfmt child and never formatted docs/project/benchmarks/index.mdx, so a run could leave the tree failing bun run check; the pass is awaited (bounded 20 s) and covers the summary too — verified by running the matrix and immediately re-running check.

12. Import-surface sweep — every namespace import either converted or documented. 49 import * as NS statements were triaged across six subagent passes under review: 38 converted to named imports in 22 files (typescript, valibot, yjs/y-protocols/lib0, Node builtins, and the adapter/query helpers), 11 kept deliberately and now carrying an intent comment — the three adapter.schema surfaces (removing them broke adapter construction; four unit tests caught it), base-adapter’s utils field, and the seven route-handler dispatcher namespaces. The safety net earned its keep: svelte-check caught one wrong conversion (an aliased import that did not exist) and one dropped member, and two namespaces were correctly left alone because they are not imports at all — the scaffolder’s v.* text is generated code, and widget-registry-service.ts documents why its qualified access is load-bearing (a named import of a test-mocked-away export is an ESM linking error, while the qualified typeof check falls back). Two improvements came out of it: the AI widget scaffolder now emits an exact named valibot import into every generated widget (widget definitions are client-bundled), and it no longer generates pipe(number(), minLength(1, …)) for a required numeric field — a string check on a numeric schema, which cannot validate. Honest scope: the client bundle is byte-identical (664 chunks, 15 181.3 KB) before and after — the converted modules were server-only or already absent from the bundle, so the win is explicit dependency surfaces, dead-import removal and refactor safety, not shipped bytes.

13. MariaDB cursor streaming enabled. Switched supportsStreaming: true in MariaDB AdapterCore (src/databases/mariadb/adapter-core.ts), allowing non-buffered streaming queries via streamMany and exports across large datasets without accumulating intermediate row arrays in Node/Bun memory.

Verification: bun run test:unit 552 files / 4 895 tests green in 18.8 s; bun run test:integration 719 tests / 76 files (694 pass, 25 skip) against a freshly built four-adapter harness; svelte-check 0 errors / 0 warnings across 6 785 files; bun run check, bun run lint:docs (263 files), lint:benchmark-mdx and risk:audit clean; project diagnostics 0 errors / 0 warnings. The two E2E specs this wave fixed are verified in the specs that documented them: profile.spec.ts green across --repeat-each=3, and design-system.spec.ts 18/18 across three repeats. The local host’s remaining contention residue is tracked as an open item in the test-status dashboard and the roadmap — not claimed as passing here.

3.33 September 2026 — Release preparation on push & a manifest writer that cannot lie

--release on the pre-push gate. bun run git push --release (optionally --release=patch|minor|major) arms a release step in .githooks/pre-push: once all nine checks pass, scripts/version.ts auto derives the next version from the Conventional Commits since the highest v* tag, the manifest is committed as chore(release): bump version to vX.Y.Z — the subject the release workflow itself writes on its manual path — and the hook prints the push that publishes it. The flag is consumed by scripts/git-safe.ts, which exports SVELTY_RELEASE for the hook, because git never forwards hook arguments to it.

Two properties are deliberate. The step runs after the full gate, never before: auto-release.yaml tags whatever package.json holds, so a bump for a tree the gate never validated is exactly the stray bump AGENTS.md §8 warns about. And the release commit cannot travel inside the push that triggered it — git resolves the pushed refs before the hook runs — so the hook prints the command to repeat instead of implying the push was the release; the bump stays local and reversible until that second push. Re-running is a no-op: within one release cycle the derivation is stable, so a second push finds the manifest already at the version and commits nothing. Skipped on the docs-only and --only-failures paths (both warn), and refused with a non-zero exit when version.ts refuses — a push that looks released but is not would be worse than a blocked push.

Known trap, observed 2026-09-27, removed 2026-09-30: a test tag v0.2.0 on the 5 July 2026 commit 6dc2d89 (that commit’s manifest was 0.0.7) was reachable, so auto derived 0.1.2 → 0.3.0 (highest reachable tag wins over git describe’s nearest v0.1.2). The tag was local only and was deleted. It was never on origin. With it gone, the highest reachable tag is v0.1.2.

A manifest writer that could not find the field it was looking at. replaceVersionField() was line-anchored (^\s*"version"\s*:…$ with the m flag), so a manifest whose version field shares a line with other keys was refused with could not locate the top-level "version" field in package.json, although the field was right there. It was found by the throwaway-repo probe that verifies the release step, not by a user report. The writer is now a depth-tracking scan (locateVersionValue()) that skips whole string tokens: a nested "version", the word inside another value, and braces or escaped quotes inside strings cannot fool it, and minified and pretty-printed manifests both rewrite byte-for-byte except for the value (10 new tests in tests/unit/scripts/version-manifest-write.test.ts; the 33 policy tests of version-bump.test.ts unchanged and green).

Escapes that printed as escapes. info() / warn() in .githooks/lib.sh passed their message through %s, so every ${BOLD}…${NC} embedded in a hint — four pre-existing failure hints plus the new release hint — rendered as a literal \033[1m in a color terminal. They now use %b; the message arguments are hardcoded strings plus validated enums, so interpreting escapes cannot mangle caller data.

Verification: the release step was exercised end-to-end in a throwaway repo (tag v0.1.0 + one feat commit): run 1 bumped 0.1.0 → 0.2.0 and committed it, run 2 no-opped, run 3 (dirty manifest) refused and returned non-zero — the exit code that blocks the push. Unit tests: git-safe-release (7), version-manifest-write (10) and version-bump (33) green; bun run scripts/version.ts --dry-run against this repository prints its decision without writing. No throughput claim attaches to this entry — it is release tooling.

3.34 September 2026 — A tail report that measured nothing, and the tails it was hiding

The verdict was an estimate of the thing it claimed to measure. verify-benchmark-local.ts graded tail stability from p99/p50, but when a result JSON lacked p50Ms/p99Ms it reconstructed them — avg/(1+(cv/100)²·0.15) and p95 + max(0.1,(p95−p50)·1.5) — and graded those. Two consequences: a verdict about an invented number is not a measurement, and the estimator was structurally biased against sub-millisecond rows, because high CV shrinks the derived p50 and the derived p99 then grows with the gap. That is why every sequential row read 🔴/🟡 while concurrent rows of the same workload read 🟢.

It was also reading a month-old file set. The report named ten files from a metric-naming scheme the harness no longer writes (findById__Sequential_.json; today’s runs write findById__Warm_Sequential_.json plus 1c variants and five further workloads). Rows are now selected by content (testFile === "competitive-workload-replica") and grouped by runId, and only the newest run is graded, so superseded names cannot resurface as phantom workloads.

What the honest version says (run 6b4c300a, measured 2026-09-27, 20 workloads after excluding the p50 = 0 seed-burst aggregate row):

Band n Workloads
🟢 EXCELLENT (<2×) 8 every read path — findById 0.34→0.49 ms, findByIdRandom, findMissing, listPlain, create (1c/seq)
🔵 ACCEPTABLE (≤3×) 7 graphql 3.19→6.47 ms, listLarge, listPlain sequential, update sequential
🟡 WATCH (≤4.5×) 3 create @8c (5.16→19.81 ms), listFilterSort sequential, mixed @8c
🔴 JITTER ALERT 2 update @8c (5.08→26.67 ms, +21.6 ms), mixed sequential (0.79→4.62 ms)

The write-path tails are real and now measurable — previously they were hidden behind an estimator that flattered concurrent rows and punished sequential ones at random. Chasing them is a roadmap item, not a claim in this entry.

Also fixed in the same file: --report=variance (documented in the test-status dashboard) fell through to the full pre-flight because the flag was matched with includes("--report"); the CLI now matches the flag family, and main() runs only under import.meta.main, so importing the module for tests no longer spawns the pre-flight. A printed-table test then caught a live defect no type checker can see: a stray + made the footer `…` + + `…` coerce its band counts to numbers → NaN. TypeScript accepts unary + on a string, so only an output assertion finds it.

Verification: tests/unit/scripts/verify-benchmark-local.test.ts grew from 3 to 23 tests — a fixture-driven contract for discovery (by testFile, newest runId only), grading (recorded percentiles only: the August row the old estimator turned into p50 0.73 / p99 3.68 now reports UNMEASURED, with p50Ms asserted not to be 0.73), the 100-sample floor, the aggregate-row exclusion, the printed table (no NaN, real P99−P50, the graded run named) and the strict gate. generateLatencyVarianceReportFromDir() is the directory-injectable core the fixtures drive.

3.35 September 2026 — The write tail attributed: WAL checkpoint I/O, not GC

The two 🔴 rows from §3.34 (update @8c 5.08→26.67 ms, mixed sequential 0.79→4.62 ms) have one root cause, and it is not the application: SQLite’s auto-checkpoint fsyncing while requests wait.

Method. PROFILE_WRITE=1 on the replica attributes each write to its spans (ns:persist, db:upd|ins:stmt, prepare-values, conv, outbox), and ~5 000 statements per run resolve a rare tail far better than a 300-sample benchmark row. The switch had never worked: the benchmark server env is a whitelist (getBenchmarkTestEnv), so PROFILE_WRITE never reached the server. scripts/benchmark-matrix/index.ts now forwards PROFILE_WRITE / LOG_LEVEL / SQLITE_* explicitly, prints a banner for override runs, and persists the server log to results/<db>/diagnostic-server.log (the in-memory buffer was dropped on success).

Where the tail is (9 726 writes, one profiled run): everything above the statement is microseconds — sanitize+validate p99 0.059 ms, encrypt 0.060, outbox 0.029, prepare-values 0.020, conv 0.016. The whole tail sits in the SQL statement: db:upd:stmt p50 0.108 ms, p99 0.716 ms, max 17.4 ms.

Interleaved A/B (five arms, one replica run each; stalls = statements > 10 ms, out of ~5 200):

Arm db:upd:stmt > 10 ms max db:ins:stmt > 10 ms max
control 31 22.5 ms 33 20.1 ms
control (repeat) 26 25.0 ms 25 25.1 ms
SQLITE_SYNCHRONOUS=OFF 0 5.0 ms 0 6.1 ms
SQLITE_WAL_AUTOCHECKPOINT=0 0 5.3 ms 0 5.1 ms
WAL_AUTOCHECKPOINT=16000 (128 MB WAL) 7 36.2 ms 16 43.2 ms

Removing either the checkpoint or its fsync removes every stall; making checkpoints rarer makes the surviving ones bigger (their cost scales with the WAL size), so “raise the threshold” is the wrong lever. SYNCHRONOUS=OFF is not shippable (power-loss durability) and AUTOCHECKPOINT=0 alone leaves the WAL unbounded — the fix is moving housekeeping off the request path (checkpoint from a separate connection or a background scheduler, size cap as the safety valve), tracked as the roadmap item above.

Also measured in the same session: with zero statement stalls the write rows’ ratios fall from 7.4× to 2.7× (update) and 3.6× (create) — the residual spread is the synchronous driver serializing eight concurrent writers, which is the second lever (worker-connection writes) and is not yet shipped. The auth-performance 500 seen once in the interrupted full matrix did not reproduce in isolation (the reject path answers 401/403 and logs Invalid or non-existent API key provided), so it is recorded here as unverified rather than fixed.

3.36 September 2026 — Tail as a rate, and five suggestions verified before building any of them

Shipped: the slow-call rate. computeStatistics() now records slowCount / slowRatePer1000 / slowThresholdMs (samples above 3 × the row’s own p50, over the raw sample) and the variance report shows SLOW‰ in place of the CV column (CV stays in the model and the JSON). Rationale: p99 of a few hundred samples is the 2nd-worst sample, so it swings several-fold between identical runs; a rate converges, and it reorders the problem immediately — first reading (one run, 20 workloads): mixed (Warm Sequential) 5.36×/175 ‰ (the worst row by frequency, only 2nd by ratio), listLarge @8c 4.45×/75 ‰, findByIdRandom @8c 3.78×/34 ‰, update @8c 7.5×/26.7 ‰, and the sub-millisecond reads at 0.0 ‰. That run was also uniformly faster than its predecessors (findById @8c p50 0.58 vs 0.68–1.21 ms) — host drift, which is exactly what the ratio metric could not survive.

Five proposals, verified before implementation (the point of the exercise — two died on contact):

Proposal Verdict Evidence
Instrument the write lane verified, top lever the same update is localcms.update_ms 0.057 ms in-process and adapter.write.avg 0.061 ms, but 2.23 ms over HTTP, while HTTP reads sit at truth.http.p95 0.525 / rest.point_read 0.84 ms with the same client — so ~1.5 ms is server-side and write-specific. The lane is also the profiler’s blind spot: no handler:* span ever fires on it.
Noop control row + rate + 3-run ensemble verified, half shipped the metric-keyed trend table (benchmark-history.ts: loadHistory/persistRun) makes ensemble grading a read-side change; the rate is shipped above; the floor (truth.http.p95 0.525 ms) shows the ratio contract cannot grade sub-ms rows — band semantics (absolute vs floor-relative) still needs a decision.
Checkpoint off the request path verified feasible, better home found hooks.server.ts already exposes inFlightRequests (graceful shutdown), so no new machinery is needed — and the watchdog/memory governor live in the background worker process (services/background/background-entry.ts), whose checkpoint cannot block the API event loop at all.
Collapse commits per write falsified an entire in-process update including its commits is 0.057 ms and adapter.throughput.write_rps is 29 525 — commits are not the expense; the 17 ms events are checkpoints (proven by the §3.35 A/B).
Worker-connection writes falsified initWorkerConnection is used only by handle-test-isolation.ts for per-worker test DBs — there is no worker-thread write path, and the DB work it would offload is 0.057 ms.

Also observed, not fixed: tests/unit/security/audit-ledger-integrity.test.ts (“200 O(1) leaf appends in

< 20 ms”) failed once at 658 ms against its 500 ms budget during a full-suite run and passes 8/8 in 119 ms alone — a contention flake of the documented kind (“perf assertions need JIT warm-up”), left for the owner.

3.37 September 2026 — The vendor-leg bisect executed: no reproducible regression in e1efe15..e4994ef

The claim under test. The external harness’s last three legs read tag v0.1.2 (e1efe15b2) as faster than main (e4994eff6) on all 11 lanes (+8 … +82 %, e.g. findMissing +82 %, findByIdRandom +52 %, graphql +36 %) while its keystone anchor moved 1.1 %. e1efe15b2 is an ancestor of main, so the claim implies a regression from one of the 8 commits in between. This entry records the local bisect: protocol-parity replica (tests/benchmarks/competitive-workload-replica.test.ts, 10 workloads, 10 000 docs, seq + 8 c, median of 3 rounds), one build per commit, SQLite and PostgreSQL.

Confound found first. The Dockerfile’s NODE_OPTIONS semi-space changed 128 → 64 inside e4994eff6 (the benchmark harness default likewise) — the vendor’s tag image ran 128 and its main image 64, a config difference shipped in the same commit box as the code claims. Every bisect leg therefore pins NODE_OPTIONS explicitly, and a 2 × 2 (tag/main × 64/128) was measured: no consistent semi-space effect (128 helped findById/graphql slightly and hurt listPlain on both commits).

Result: no persistent regression. The two large single-run dips both recover inside the range — which a real regression cannot do — and both failed to reproduce on re-run:

commit list_plain warm 8c note
e1efe15b2 (tag) 13 307 / 13 792 (2 runs) baseline
54bd19f76 13 310 flat
5be703223 9 786 re-run 10 916 — not reproduced
db6becec6 12 770 recovered
d091b2178 11 332
60fd09c3f 9 804 re-run 11 229 — not reproduced
d9060a465 11 712 recovered
e4994eff6 (main) 11 309 / 13 700 (2 runs)

The noise floor is the finding — identical builds, repeated runs, same session, this host:

lane (phase) run A run B Δ
findById warm-seq (tag) 4 993 4 613 −8 %
findByIdRandom warm-seq (tag) 2 818 3 424 +21 %
listLarge warm-seq (tag) 1 779 1 277 −28 %
findMissing warm-seq (tag) 3 859 5 184 +34 %
findMissing warm-seq (main) 3 423 5 440 +59 %
graphql warm-seq (main) 619 456 −26 %
findById warm 8c (main) 10 905 13 332 +22 %
listPlain warm 8c (main) 11 309 13 700 +21 %

PostgreSQL (the vendor-likely engine) agrees: cold.findByIdRandom on main across three runs reads 2 212 / 2 438 / 2 887 against tag’s 2 702 / 2 630 (2.7 % spread between the two tag runs) — the one lane where main looked persistently slower dissolves into main’s own 26 % spread. The vendor’s smallest attributed deltas (+8 … +20 %) sit below this host’s run-to-run floor; its largest (+52 %, +82 %) are not reproducible in either engine with ≥ 2 samples per side.

Conclusions. (1) No commit in e1efe15b2..e4994eff6 shows a persistent, monotone lane regression — the local replication of the vendor protocol does not reproduce the tag > main ordering on SQLite or PostgreSQL. (2) The only structural change in the range that could act globally is the Dockerfile semi-space 128 → 64, and the local A/B says it does not explain a uniform slowdown. (3) A vendor-side bisect needs alternated pairs (≥ 3 per side, identical NODE_OPTIONS) per the protocol this project already requires for that class of claim; single-session legs carry a floor wider than the attributed effect. Measurement hygiene: bisect legs wrote to an isolated RESULTS_DIR (no trend-store rows); the two ad-hoc runs that did record (42 rows) were removed from history.sqlite.

3.38 September 2026 — The write lane’s “unlocated 1.5 ms” resolved: it was never server-side

The claim on trial (§3.36, first proposal): an update measured localcms.update_ms 0.057 ms in-process but 2.23 ms over HTTP against 0.525–0.84 ms HTTP reads — “so ~1.5 ms is server-side and write-specific”. Resolved with a dedicated probe (tests/benchmarks/probe-write-attrib.test.ts): the same operation measured three ways — in-process db.crud.update at c=1 and c=8, and an HTTP PATCH through the lane with x-srv-split/x-srv-dur — SQLite, production build, warm session, 300 iterations per arm.

measurement (p50) run 1 run 2
in-process crud.update c=1 0.063 ms 0.067 ms
in-process crud.update c=8 0.576 ms 0.606 ms
HTTP wall (bridged) 1.476 ms 1.521 ms
HTTP wall (raw write lane) 1.251 ms 1.306 ms
HTTP x-srv-dur (server-stamped) 0.380 ms 0.360 ms
HTTP dbwrite (adapter phase) 0.160 ms 0.150 ms

Why the 1.5 ms never existed. Both halves of the original pairing were wrong: the 0.057 ms baseline was crud.atomicIncrement — a single count = count + 1 statement outside the SDK pipeline (validation, hooks, audit/outbox, invalidation, envelope) — not the update the lane runs; and the 2.23 ms arm was a client-observed HTTP wall. Against the lane’s own stamps the server cost of that update is 0.35–0.40 ms total, 0.15–0.16 ms of it dbwrite; ~0.9–1.1 ms of the client number is client fetch + loopback transport — the same class as the ~1.1 ms per-request client floor the read row already documents. At 8c the write row is dominated by write serialization, and the in-process c=1→c=8 pair proves it is engine-side, not HTTP-side: the same call goes 0.063 → 0.576 ms (×9) without a request in sight.

Transport A/B (both orderings). The opt-in raw write lane (no adapter-node bridging, SVELTY_FAST_LANE_WRITE=1) measured −15 % client wall (1.48/1.52 → 1.25/1.31 ms) with server-stamped time unchanged (0.38/0.40 → 0.35/0.36 ms) — reproduced with the arms in both orders. That is the paired transport evidence the write lane’s default flip was waiting for; the remaining gate is the BENCH_VERIFY_RAW=1 byte-equivalence proof plus a c=8 paired run.

3.39 September 2026 — Lane responses get their compression back (and the >64 KiB tier gets its first honest number)

The gap. Fast-lane responses bypass handleCompression (LANE_BYPASSED_HOOKS) and serveTurboCacheEntry can only serve an encoding that was attached to the entry — so every lane-served list left the socket as identity bytes. Measured 2026-09-28 through the zstd audit: a 260 791 B body answered a zstd-only client uncompressed, and the whole zstd/co-tenant scenario in api-latency.test.ts reported api.zstd.available=0 (skipped) — the tier was unmeasurable, not slow.

What shipped. src/hooks/response-compression-stash.ts — one shared variant implementation for the read lane and the API dispatcher, with a precise contract:

  • Hits are free: list entries carry compressed variants (zstd with the CMS dictionary, br/gzip ≤ SYNC_MAX_SIZE); serveTurboCacheEntry already negotiates Accept-Encoding per request. The response-cache’s internal precompute turned out to be commented-out dead code, so lane entries had never carried a variant at all.
  • First re-hit warms lazily: point-read misses are high-cardinality and skip the CPU; the second request for any key schedules the stash — a re-read is the proof that the key is worth compressing.
  • Miss path inside the buffered budget: the lane compresses ≤ 64 KiB synchronously (exactly the pipeline’s own sync tier) and seeds the variant into both the response and the cache entry.
  • Safety rails: in-flight guard per key, expanded output dropped, a body that produced nothing is marked attempted (compressed: {}) so later hits do not retry, preserveStale keeps a concurrent write’s staleness decision (the old set() forced stale = false), and a superseded body is never overwritten (body/etag re-check before the re-set). entryBytes() now counts variant bytes, so the L1 budget no longer drifts when variants are attached.

Beyond 64 KiB the lane declines to the pipeline (compress-while-sending, no full-body wait). Buffering the same 255 KiB body in-lane measured 10.8 ms/request, 183 RPS, co-tenant p95 0.33 → 2.50 ms — rejected. The pipeline’s streaming tier is now measurable end-to-end and lands at 22.7 ms warm / p95 24.8 ms / 87 RPS with co-tenant p95 0.30 → 7.39 ms on this host (cache-busted, so every request pays a fresh compression). That is a property of the app-wide streaming compression tier — now visible for the first time — not of the lane change; a level/threshold decision for > 64 KiB bodies is the next lever, tracked in the roadmap.

Verification: tests/unit/hooks/response-compression-stash.test.ts (9 cases: gates, builder guards, hit-stash, supersession, staleness, size no-op) + 51/51 across the neighbouring hook suites; the api-latency probe now reports zstd active: … body 254.7 KiB (> 64 KiB async tier) instead of a skip.

Follow-up A/B (same day) — the level/threshold hypothesis is falsified. The 22.7 ms / +7.4 ms co-tenant in that probe invited a compression-settings fix; isolation says otherwise. zstdCompress on a 244 KiB body measures 0.30–0.55 ms at every level 1→15, and the CMS dictionary artifact (static/dictionaries/cms-payloads.dict) is absent in-repo, so {dict} is a no-op — neither level nor dictionary is a lever. End-to-end, the same 256 074 B list: identity warm 0.95 ms · identity miss 14.10 ms · zstd warm (stashed variant) 0.72 ms · zstd miss (lane declines → pipeline, compression included) 15.16 ms, co-tenant point read 0.38 → 0.65 ms. So compression is ~1 ms of a ~15 ms miss — the cost is the cache-busted miss path itself (DB read + build + 256 KB serialisation/drain), and cached hits serve the variant for free at sub-millisecond latency. No level/threshold change was warranted; the open lever is the list-miss path, not the encoding.

Round 2 (same day) — the miss path decomposed, and a wrong turn corrected. Lane marks (x-srv-split) plus ENABLE_HOOK_TIMING=1 on a 199-row / 257 027 B list (SQLite) read: lane HIT 0.79 ms (ttfb 0.79, drain 0.00) vs miss ttfb 14.98 / drain 0.23 ms — the server is late, not the wire — and the whole nested hook chain accounted for only ~3.7 ms, so the rest was the row build (≈0.055 ms/row). Two fixes:

  • streamingJsonResponse (and streamingExportResponse) are pull-driven now. The old writer ran a start() loop and slept a hard-coded setTimeout(10) whenever desiredSize <= 0 — a count-based stream’s HWM is 1, so that was most iterations (the limit = MAX_PAGE_SIZE path had been measured at ~3.1 s per request), and the export writer yielded setTimeout(0) per record (a 10 k-row export paid ~10 s of timers). The stream now calls pull() only when it wants data: backpressure is structural, zero timers. Pinned by tests/unit/api/streaming-json-response.test.ts (identical bytes, maxItems, error marker, and a 500-item drain that would take ≥ 5 s under the old sleep).
  • The lane no longer declines > 64 KiB lists to the pipeline. Declining meant the lane had already built the rows, discarded them, and the pipeline rebuilt them: 15.0 ms vs 10.8 ms for the same list, co-tenant p95 7.39 → 2.50 ms. The lane now compresses the negotiated encoding itself — sync at ≤ 64 KiB, one-shot async (libuv pool, compressAsync) above — and seeds the variant into the response and the entry.

Measured after both fixes (api-latency zstd audit, SQLite, 254.7 KiB cache-busted list): warm 22.7 → 11.4 ms, 87 → 175 RPS, co-tenant p95 during the burst 7.39 → 2.93 ms; turbo point read in the same run 0.711 ms / 11 097 RPS. The remaining ~7 ms of the 11.4 is the SDK list build itself (row hydration + per-row JSON) — the list wire plane is the next lever, recorded in the roadmap.

8c re-verification, same session (BENCH_ZSTD_CONCURRENCY=2 vs =8 on one build — the audit row is knob-controlled now and the matrix forwards it): the row goes 13.0 ms / 153 RPS (2c) → 49.4 ms / 154 RPS (8c). Latency scales with concurrency while throughput stays flat, so the large-list miss path is saturated at ~150–175 RPS, and the ceiling matches the synchronous SDK row build (~6–7 ms of event-loop work per request, interleaved across 8 in-flight builds, while compression itself is off-thread). The point-read rows at the same 8c kept 9 676 RPS, which isolates the cap to the list build — the empirical case for the list wire plane below. (The c=2 point re-measured 13.0 / 153 here against 11.4 / 175 in the earlier session — host drift between sessions is 7–59 % on this box.)

Same day — readiness truthfulness, a deterministic list order, and the list wire plane measured (then shelved).

  • Health is truthful now. Three responders reported overallStatus: "READY" from DB presence alone while the boot cache-warming subsystem was still initializing — every harness (and operator probe) then measured the warm-up itself. The turbo pipeline’s buildHealthResponse, the dispatcher’s HYPER-TURBO health branch and the hooks.server responder now all emit the derived state plus the strict ready predicate (isSystemReady(): operational state AND the boot pre-warm settled), and the benchmark harnesses (isHealthReady in the matrix, the standalone wait) require ready !== false before measuring. Verified live: {"status":"healthy","overallStatus":"READY","ready":true,…} on the bench server; the waits still pass.
  • Unsorted list reads have a deterministic order. defaultListSortOption() (updatedAt desc, _id desc) is now the SDK find default for every non-keyset read. Without an ORDER BY the engine picks the access path, so the same logical query could return different LIMIT/OFFSET windows under two plans (proven: SELECT rowid,_id served from the _id auto-index vs SELECT <all cols> served from a table scan, same table, different first rows) — a real pagination bug, and the reason a compiled list statement could never be byte-stable. The order matches the keyset default and rides the (tenantId, updatedAt, _id) index (no temp B-tree, verified via EXPLAIN QUERY PLAN). Note for a rolling deploy: the default-list response cache key stays …:find:default_50, so entries cached under the old (unspecified) order live out their TTL — bodies are otherwise identical.
  • The list wire plane (engine-built data JSON) was implemented to byte parity and measured — it loses. On SQLite it was built to completion (row = json_object over the physical selection + json_remove blob splice; fail-closed bad_rows guard for NULL/non-object blobs; the lane frames the exact envelope incl. meta.executionTime = BaseAdapter’s constant wrap meta and the _collection-only-when-rows rule) and reached provable byte parity: the integration differential suite compares wire output against the domain envelope byte-for-byte, and an HTTP A/B on the bench server (x-wire: list marker) returned 37 916 B == 37 916 B. Then the timing: on a fresh bench DB (201 rows), a 50-row × ~750 B list measures wire p50 5.05 ms (SQL alone 1.7–2.9 ms) vs domain p50 2.43 ms — SQLite’s JSON functions are text-based (render + json_remove re-render + splice copies ≈ 58 µs/row) and node:sqlite is synchronous anyway, so nothing moves off the event loop. On PostgreSQL/MariaDB the same idea cannot be byte-identical at all: json_build_object / JSON_OBJECT render spaced JSON ({"a" : 1}) and jsonb additionally reorders keys, so V8 JSON.stringify bytes are unattainable without value-corrupting string surgery (the shipped PG point wire already serves engine-formatted bytes, so parity there means stability, not identity); MongoDB has no engine-side JSON without $function. Conclusion: the remaining ~7 ms of a large list miss is not removable by engine JSON — the saturation evidence (153 RPS at 2c / 154 at 8c) stands, the lever does not. The SQLite implementation, the lane branch and the interfaces were removed again (no dead code); the reusable pieces kept are the deterministic order and the point-wire delegation fix below.
  • Point-wire delegation in the tenant guard. createTenantGuardedCrud builds an explicit method list, so under MULTI_TENANT the wrapper hid findPointWireStream and every lane read silently fell back to the Domain Plane. The wire method is now delegated through the same tenant-options guard as every other op.
  • Row-shape lever profiled and falsified (same day). A faithful stage replay of the list build (199 rows × ~1.3 KB blob, 16 keys/row, real convertDatesToISO + trimListEnvelope + stringify) costs 0.66 ms total (3.3 µs/row): blob JSON.parse 0.27 ms, parse+flatten+date/bool conversion 0.38 ms, trim copy 0.03 ms, JSON.stringify of the 364 KB envelope 0.31 ms. A blob-only projection shape saves ~0.2 ms, dropping the blob entirely ~0.5 ms — the row shape is not where the list cost lives. The 310 KB (199 × ~1.5 KB) list’s real x-srv-split build mark is 6.4 ms, ~10× the stage replay: the remaining mass is string materialisation (body + ETag hash + variant handling) and the GC that rides on it, not per-row parsing. Concurrency check on that shape (cache-bypassed, identity): p50 8.64 / 9.28 / 12.99 ms and RPS 116 → 194 → 567 at c=1/2/8 — the earlier “flat ~153/154 RPS” saturation reading did not reproduce outside the zstd audit’s own setup, so that number should not be quoted as a general list ceiling until it is re-measured with the compression path separated out. Harness note for whoever picks this up: fresh DB_NAMEs currently 401 at benchmark admin login (authUsers absent after pre-boot seeding, boot logs a ThemeFileSync failure) while existing DBs work — flagged, not attributed to this change.

3.40 September 2026 — The 380 KB list miss’s build mark, found and cut: an 8 ms FNV fold → native digest (−84 %)

The open measurement, closed. §3.39 left one explicit next step: re-measure the list saturation with the compression path separated from the build path. The new diagnostic probe tests/benchmarks/probe-list-compression-ab.test.ts does exactly that — Accept-Encoding: identity vs zstd at c=1/2/4/8 on a 199-row / 381 681 B list (220 × ~1.5 KB rows seeded through the API into BenchmarkStable, one unique probe= cache key per request so every request is a true miss and single-flight coalescing cannot fold the arms together), reporting client p50/RPS plus the x-srv-split deltas (db−lookup, build−db, cachewrite−build, serve−cachewrite).

Result, before any change (SQLite, production build): buildΔ (trim + stringify + etag) is 6.2–6.4 ms on both encodings — compression is not in the build; cacheΔ isolates it: identity 0.14–0.16 ms vs zstd 0.96 / 7.16 / 15.6 / 29.1 ms at c=1/2/4/8 (the awaited compressAsync plus its worker-pool queueing; server x-srv-dur p50 grows 7.5 → 35.5 ms on the zstd arm). The zstd response is 5 132 B where identity is 381 681 B, so at c=8 the client is faster on zstd (p50 53.2 vs 63.8 ms, 136 vs 119 RPS) despite the server paying more — the compression wait buys a 74× smaller transfer and pays for itself on the wire.

The build mark profiled: an in-process profile of the exact lane steps on the captured 381 681-char body: Buffer.byteLength 0.004 ms, Buffer.from 0.083 ms — and generateContentEtag (FNV-1a 64 over UTF-16 code units) 8.2 ms (Bun) / 3.9–7.9 ms (Node). It is the entire build segment, and the single most expensive per-request op on the large-list path. Two same-output rewrites were measured and rejected: int-limb fold 1.03×, reciprocal-multiply carry 1.00× — FNV-1a is a serial multiply-accumulate chain, so arithmetic micro-optimisation cannot move it; byte-wise FNV (different output) only 2×.

The fix — size-gated native digest. generateContentEtag keeps the FNV fold byte-identical below 1 KiB (small settings/GraphQL/point payloads: zero validator churn), and above it returns "s256|<sha256-hex>" computed with crypto.hash("sha256", body, "hex") (fallback createHash). The T15 contract is unchanged — the validator still covers the entire body, so any content change flips it — and the digest is 20–50× faster at every measured size (0.0013 ms @1 KiB, 0.10–0.21 ms @380 KB). One function backs lane, dispatcher, GraphQL and turbo-stash etags, so the win lands on every large-body miss path at once.

Result, after (same probe, same shape): buildΔ 6.4 → 0.94–1.01 ms (−84 %); identity p50 c=1 8.84 → 3.26 ms, RPS 99/119 → 252/343 at c=1/c=8; zstd cacheΔ @c=8 29.1 → 7.75 ms (less event-loop contention in front of the worker pool); in-process etag cost 9.8 → 0.102 ms. Regression test: tests/unit/core/request-classifier-and-response-cache.test.ts pins "…16-hex" below the threshold, "s256|…64-hex" above, plus determinism and sensitivity.

Harness findings (the flagged 401, diagnosed). The “fresh DB_NAMEs 401” note from §3.39: in local profile the pre-boot seeder creates the sandbox DB through the env-only adapter path, which materialises only the content/collection schema — no auth_users/system_preferences — so seedBenchmarkState’s createUser silently no-ops (its result is unchecked) and the server can never authenticate. Snapshotting a completed DB (VACUUM INTO config/test-database/<name>.sqlite) restores it. Also: seeding POSTs must carry content-type: application/json — Bun’s fetch sends text/plain for string bodies and the write lane then 500s with Unexpected end of JSON input (probe-side bug, fixed in the probe).

3.41 September 2026 — Harness DBs stop landing in the user’s folder, composite indexes get a policy, and the update lane’s residual is proven to be the durable commit

Three threads, one honest measurement discipline: no claim without a falsification run.

1. The E2E/benchmark databases that appeared in config/database/ (root cause: a require). The user’s live-data folder had accumulated e2e_auth_test.sqlite, bench_parent.sqlite, benchmark_shared.sqlite, sveltycms_test.sqlite and a set of probe_* scratch DBs — none of which belong there. Fingerprints showed 36-table, 0-row shells (auth/settings empty, only worker-written svelty_jobs rows), i.e. harness debris, not user data. Root cause proven from the worker’s own log ([DB] Registry resolved connection string: config/database/probe_test_whoF.sqlite): resolveSqlitePath() (src/databases/config-state.ts) created the test folder via a bare require("node:fs") inside the try that also returned the test path. In the esbuild-built background-worker bundle that require throws (Dynamic require of "node:fs" is not supported), the catch swallowed it, control fell through the if (isTest) block, and the network-address branch answered config/database/<name>.sqlite. Fix: static ESM imports (they cannot throw), best-effort mkdirSync, and the return outside the try so a mkdir failure can never re-route the path again. The name heuristic now calls the shared isIsolatedTestDbName() classifier that enforceTestSafety() already uses (test/bench/e2e/_functional, minus production-like names) instead of a divergent includes("test") || includes("benchmark") copy. Verified after rebuild by running the worker directly: harness flag + probe_test_whoG → config/test-database/…, harness flag + plain probe_plainA → config/test-database/…, no flag + probe_plainB → config/database/… (true-dev behaviour preserved). Regression guard: tests/unit/databases/resolve-sqlite-path.test.ts — 10 tests covering flag routing, classifier names, dev names, explicit POSIX/Windows/relative hosts, .sqlite canonicalization, and — because this is the actual incident — a real mkdir failure (a process.cwd spy pointing at an existing file, so no fs mocking can be dodged). The guard was falsified before shipping: moving the return back inside the try makes it fail with expected 'config/test-database/probe_test_worker.sqlite' / received 'config/database/probe_test_worker.sqlite' — exactly the incident’s signature.

2. The covering composite index now has a policy (all three SQL engines). createModel used to provision (tenantId, status, <col>, _id) for every materialized column; each one is maintained on every write, because updatedAt is indexed and rewritten by each update, so PostgreSQL can never take the HOT path and must insert into all indexes on the table. A psql A/B on a narrow table (3 vs 4 indexes, 2 000-row batches, alternating orders) measured that cost directly: +11–17 % per extra index (≈ 0.7 µs/row/index). The policy — shared by PostgreSQL, MariaDB and SQLite via buildCompositeIndexColumns() in src/databases/core/drizzle-sql-helpers.ts — provisions the composite only for declared query targets: indexed: true fields, numeric fields (the default listFilterSort sort target), and the publishedAt base column. Equality-lookup base columns (collection, slug, locale) and storage-only columns (unique, materialize: true) keep their single-column index (still filterable) but no longer pay for a covering twin; legacy twins are dropped explicitly, following the existing …_tenant_status_updated twin-drop precedent. The default list order is untouched — it is served by …_tenant_status_updated_id, which the policy always creates. On the 113 k-row benchmark table the policy takes the index set 15 → 12 (pg_indexes, verified after a shipped-code boot). Coverage: tests/unit/databases/composite-index-policy.test.ts (eligibility + column-set contract) and the cross-engine integration block Composite-index policy — {engine} in tests/integration/databases/dynamic-field-filter-index.test.ts (a numeric field keeps its composite; unique/materialize/lookup columns do not; their single-column index exists) — green on SQLite and PostgreSQL.

3. Where the update lane’s residual actually goes — the durable commit, measured. The write-split probe (tests/benchmarks/probe-write-split.test.ts, PostgreSQL, production-mode server, 2 400 iterations at 8c) puts the update lane at x-srv-dur p50 3.67 ms / avg 3.78 ms, decomposed as security 0.014 · parse 0.081 · schema 0.000 · prep 0.028 · encrypt 0.011 · dbwrite 3.548 · postwrite 0.026 · serve 0.058 ms. Every app phase sums to ~0.16 ms (4 %); dbwrite owns 94 %. Then the same statement was measured with and without the durability wait (psql, single-row autocommit = the app’s pattern): 1.566 / 1.658 / 4.496 ms with synchronous_commit=on vs 0.310 / 0.347 / 0.430 ms with off — i.e. ~75–85 % of dbwrite is the WAL fsync/commit wait on this host’s Docker volume, and 200 rows in one transaction cost 42–44 µs/row for all the actual tuple+index work. That is why neither the composite policy nor the earlier numeric-materialization change can move the lane here: the effect sizes are single-digit µs against a ~3.7 ms commit-dominated op, and the replica’s own pair (update 1 280 → 1 406 RPS) also moved the read lanes +21–32 %, i.e. host drift, not policy. Both facts are now recorded as such — the policy ships as a write-amplification/WAL reduction, not as a latency claim.

Harness hygiene found on the way: a leftover vite dev server for the external benchmark dashboard had been burning CPU since 2026-09-26 (57 631 s of CPU time), one more reason the recorded cross-day spreads are wide; and a 20-minute build stall reproduced once and then vanished on re-run (no product change involved).


3.42 September 2026 — Session risk, lane threat scan, SSO metadata, WebAuthn ceremony

  • Read lane: a warm collection GET or HEAD runs wafGuard.inspectEvent and the honeypot / non-local scanner response before any cache or database read. handleSecurity (heap shedding, the Redis WAF limiter, the GraphQL shield) stays off that lane. No new benchmark file; the scan is the same linear walk the write lane already calls.
  • Session risk: a changed user-agent steps mutations up (SESSION_RISK_STEP_UP, or /login?reason=step-up for pages) until POST /api/user/sessions/reauth verifies the password and adopts the current browser. An IP-only change is logged. Reads stay available.
  • SSO: OIDC session metadata is written to the session cache (sso:session:{id}, 7 days) as well as process memory, so RP-initiated logout can finish on another node. Back-channel logout still walks the sessions this process has in memory.
  • WebAuthn: register and sign-in reject a foreign origin and a missing user-presence flag. The assertion signature is checked over the raw client data bytes.
  • Self-assessment, re-run 2026-09-29: the August weights still total 99.93/100 (~99.9%). bun run test:security passed 343 tests. bun run risk:audit passed 5/5 after the read redaction path was switched to canonical isAdmin() (the first audit pass had one admin-role-fragmentation finding). The two 99.8 rows were not raised. The figure is the internal checklist. A dated penetration test by someone outside the project is still open, and it is not one of the six weights.

3.43 September 2026 — Missing columns on an existing database, and the warm write lane’s remaining URLs

  • Schema boot: CREATE TABLE IF NOT EXISTS does not add a column to a table that already exists. A boot that only ran it stored the spec fingerprint anyway, so the next start skipped the pass and left the live table behind the spec. MariaDB bench_parent.auth_sessions was missing amr and mfaVerifiedAt; session insert failed and admin login returned 401 Failed to create session. Boot now adds any spec column an existing table does not have. A bare spec fingerprint triggers that pass once. After it succeeds the stored marker is computeColumnSyncedFingerprint, and a later boot skips both the DDL pass and the column scan. A declared renamedFrom is replayed. The dropped v0.0.8 repairs are still not. Documented in Upgrading. Test: tests/unit/databases/schema-spec-parity.test.ts.
  • Warm writes: batch, bulk, increment, status, and POST /api/graphql join the SvelteKit collection write lane when the session is warm and the caller is an admin. The socket write lane stays opt-in. Prefer: return=minimal on increment and status skips the row read-back on all four adapters, the same contract create and update already had. The default response is still the full document. The PostgreSQL full-document update remains the durable commit described in §3.41.

3.44 September 2026 — Schema model

The lean collection schema model shipped in src/content/schema-model.ts, src/content/schema-hooks.ts, and src/databases/core/column-renames.ts. Documented in Schema model. Relation values stay ids. There is no new foreign-key index on the write path, because each extra index is maintained on every update (§3.41). A full-document update is still the durable commit. The remaining model surfaces (compiled relation filters, nested writes, onDelete, virtual, omit) are tracked in the roadmap.

  • GraphQL lists take where: JSON, orderBy: String, and take: Int in one operator language (one JSON argument, not a generated input type per field). parseOrderBy accepts field, +field, -field, field:asc|desc, and field asc|desc and rejects unknown or non-orderable fields (ORDERBY_UNKNOWN_FIELD, ORDERBY_FIELD_NOT_ORDERABLE). Inverse relations take where and filter loaded rows in memory; isFilterable: false rejects a filter on that field. Where shape cap: 8000 characters, depth 6, 64 nodes.
  • Hooks, on the collection and on field.hooks: resolveInput, validate (many errors, one abort before the write), beforeOperation, afterOperation, in addition to beforeValidate / afterValidate. Order is field and list resolveInput, beforeValidate, field and list validate plus the numeric/exact gate, beforeOperation, afterValidate; scheduleAfterOperation runs the post-commit afterOperation without awaiting, and a failure is logged at debug. prepareWritePayload passes schema.fields whenever a field declares hooks, and ensureSchemaHotFlags derives _hasHookFields / _hasAfterOperationHooks so hook-free schemas pay nothing.
  • decimal, bigint, calendarDay, and bytes are exact string columns (materializedSqlType: SQLite TEXT; PostgreSQL and MariaDB VARCHAR(64), VARCHAR(10), and TEXT) and do not earn a covering composite index unless indexed: true. shouldMaterializeField materializes them by default, so they are real columns rather than blob fields.
  • Schema actions { permission, increment?: { field, amount }, patch?, run? } are checked by the action permission in CollectionsNamespace.runAction. REST is POST /api/collections/:id/actions/:name with { entryId, input }; GraphQL is runCollectionAction. REST does not also require collections:write. An undefined action is 404 ACTION_NOT_FOUND; an action without a permission is admin-only (ACTION_FORBIDDEN).
  • renamedFrom on a system ColumnSpec or a collection field renames when the old column exists and the new one does not, before ADD. column-renames.ts matches the live catalog case-insensitively and issues the rename in the stored case; a failed rename is retried on the next provisioning pass. Mongo uses $rename once per process when the model is created. The dropped v0.0.8 repairs are still not replayed unless a column declares renamedFrom. Tests: tests/unit/databases/column-renames.test.ts, tests/unit/content/schema-model.test.ts, and tests/unit/databases/schema-spec-parity.test.ts.
  • isFilterable / isOrderable are booleans or functions of { user } and are enforced on GraphQL filters and sorts. cacheHint: { maxAge, scope } sets GraphQL Cache-Control (tightest maxAge, private wins); a private hint is not stored in the shared GraphQL response cache.
  • Row ownership ownership: { field }: find, count, and findStreaming scope to field === user._id before the cache key (admins and system scope bypass; a caller without a user id matches nothing), and presentRead drops a non-matching point-read row on a copied envelope so the shared cache entry is never mutated or poisoned. Tests: tests/unit/sdk/collections-ownership.test.ts.
  • singleton: true forces _id singleton:<collectionId> in create; a second create answers 409 SINGLETON_EXISTS. The _id primary key makes a concurrent double create fail on the insert instead of persisting two rows.

3.45 October 2026 — Durable-commit parity for every SQL engine, UNNEST/JSON_TABLE bulk writes & the system index overhaul

The 2026-09-23 engine-knob probe ended with “deliberately NOT shipped” because the knob was cluster-global. It is now shipped the safe way: per-connection GUC parameters, so only the CMS pool relaxes durability and production keeps the server default unless the operator opts in.

  • PostgreSQL durability knob wired — pgConnectionParameters() (src/databases/postgresql/adapter-core.ts) passes PG_SYNCHRONOUS_COMMIT/DATABASE_SYNCHRONOUS_COMMIT (plus PG_WORK_MEM, PG_JIT default off) into every pool (primary, tenant pools, replicas). Self-measured on the local WSL2/Docker host (2026-10-04): update dbwrite p50 9.76 ms → 2.3–2.9 ms (96 % → ~70 % of the request), competitive update lane (warm 8c) ~1.5 k → 2.0–2.6 k RPS.
  • PostgreSQL UNNEST bulk writes — insertMany/rawInsertManyReturning/rawBulkUpdate bind one text[] array per column with element-wise casts; constant SQL per column set (plan-cache friendly), presence arrays keep explicit-NULL vs omitted semantics, data_mode patch/set/keep preserves per-row JSON blob safety. Param count drops from 2×N scalars to 1 array/column.
  • MariaDB JSON_TABLE bulk update — the MariaDB twin of the PG UNNEST path (UPDATE … JOIN JSON_TABLE, version-gated 10.6+, CASE-ladder fallback below). Fixed live (2026-10-04): the JOIN was pinned to utf8mb4_unicode_ci, which raises error 1267 on tables created under the MariaDB 12 default collation (utf8mb4_uca1400_ai_ci) — the error was silently swallowed and every heterogeneous batch fell into the 100× per-row loop (68.4 ms). The JOIN now compares COLLATE utf8mb4_bin (works for both collations) and the catch logs instead of vanishing. Self-measured after the fix: BULK UPDATE (100 heterogeneous) 68.4 → 3.6 ms (~19×, 14 → 267 RPS), contract suite 18/18. Measured and deliberately NOT shipped (2026-10-04): the INSERT twin (INSERT … SELECT FROM JSON_TABLE) lost the A/B — BULK INSERT (100) 2.70 → 3.24 ms (+19 %) and seed burst 7,676 → 4,899 docs/s (−36 %) — because the multi-VALUES path binds values natively while JSON_TABLE pays a full JSON document parse per batch; the UPDATE twin won because it replaced N per-row statements, not direct binding. The finding is documented in rawInsertManyReturning.
  • Durability parity baked into the harness DBs (tests/docker-compose.yml): PostgreSQL synchronous_commit=off + buffer tuning; MariaDB innodb_flush_log_at_trx_commit=2 (the MariaDB twin — there is no per-session GUC, so the server flag is the only knob). Self-measured competitive replica (warm 8c, 2026-10-04): MariaDB update 1 269 → 1 918 RPS (+51 %), create 1 466 → 2 083 RPS (+42 %); seed burst 5 686 → 7 676 docs/s.
  • System index overhaul (single source of truth system-schema-spec.ts + drizzle schemas + renderer): audit_logs timestamp btree → BRIN + (tenantId, timestamp DESC); content_nodes children index (tenantId, parentId, position, _id); content_drafts latest index (contentId, version DESC); content_revisions UNIQUE (contentId, version); auth_users email unique NULLS NOT DISTINCT; redirects UNIQUE partial (tenantId, source) WHERE active = TRUE with legacy dedupe + drop extensions. MariaDB/SQLite equivalents kept in parity. Provisioned and verified via psql/schema-spec-parity (48 files / 347 tests).
  • Benchmark scale guard recalibrated (2026-10-04): the denominator was findByIdRandom, which now runs at the HTTP+DB floor (~8.8–9.5 k RPS), structurally capping any DB-bound write ratio at ~0.20. The guard now compares writes against listLarge (the heaviest DB-backed read lane, 5.1–6.5 k RPS) at a 0.25 threshold: PostgreSQL passes at 0.40/0.40, MariaDB at 0.31/0.27 (both self-measured 2026-10-04, warm 8c).
  • Fast-lane oversized-body fix (index.server.mjs): content-length > 256 KB now rejects with a proper 413 before the lane reads the body (no more FAST_LANE_PAYLOAD_TOO_LARGE 500s).
  • UPDATE ?fields= projection (2026-10-05): PATCH /api/collections/:id/:entryId?fields=_id,count,updatedAt prunes the SQL RETURNING read-back to the requested physical columns on both write paths (fast lane + full pipeline) — the JSON data blob is never re-read/de-TOASTed for callers that only need the changed fields. Blob-only names are resolved away (resolveUpdateProjection, unit-tested); an empty projection falls back to the full representation. Verified end-to-end by a response-shape guard in the replica benchmark (BENCH_UPDATE_FIELDS=_id,count,updatedAt): projected responses contain exactly the requested keys. Self-measured on the small-blob replica the lane sits within host noise of the full-return row (2,840 vs 2,964 RPS, warm 8c) — the gain scales with blob size/TOAST, which is the 100k-doc external-harness regime. The other two Keystone-gap levers were verified as already implemented: pure physical-column updates emit no COALESCE(data,…) || $n merge clause, and the audit hook already fire-and-forgets (void flush + 15 ms coalescing timer, never awaited by the response).
  • Find/write path lean-out, probe-first (2026-10-05): probe-point-read-split ran per engine — cold misses show build = 0 on all four (PG db p50 0.54 ms, MariaDB 0.56, SQLite 0.06, MongoDB 0.75), i.e. the wire statement serves every admissible point read and the JS copy chain (date-conversion clone, blob flatten, _collection stamp, HTTP trim, second stringify) never executes on that path — per the probe’s own decision rule the find path stays alone. Two safe items shipped instead: (1) the domain fallback findById now converts the fresh Drizzle row in place (nothing else holds a reference — the lane never caches the converted object); (2) the warm write lane admits an author carrying a fresh collections:write bit (isSimpleWriteLaneAuthorized, mirrors the read lane’s rule) — previously a signed-in author with a warm session got a hard 403 on simple POST/PATCH and never reached the permission map; unauthorized/stale callers still fall through to the full pipeline while the body is unread, and WAF/CSRF/session-risk/field-guard checks all stay. Declined with measured reasons: the _collection-stamp skip (the wire path never runs it — one assignment on non-admissible reads only) and the updatedAt-on-counter-only experiment (commit-bound lane; changes newest-first sort semantics).
  • Lane-lean rate limit: local enforcement, async Redis ledger (2026-10-05): the warm collection write lane opts into rateLimit({ asyncRemote: true }) — the in-memory token bucket enforces synchronously (pure math, no I/O) and the identical spend propagates to Redis without awaiting it, so a mutation never pays the Redis round trip on its critical path. The limit itself is unchanged and a second process still sees the spend a tick later; GraphQL writes and the full pipeline keep the awaited Redis enforcement. Self-measured paired medians (probe-write-split, SQLite, Redis up, same session, 3 runs/side): warm create srv-dur p50 1.860 → 0.360 ms (−81 %), warm update 1.485 → 0.310 ms (−79 %); PostgreSQL create/update sit at 2.93/2.96 ms p50 with the fix — the ~2.4–2.8 ms commit remains the limiter there, exactly as the review predicted. Unit-tested (async-remote.test.ts, 4 cases: returns before Redis resolves, local exhaustion while Redis pending, awaited default path unchanged, memory fallback when Redis is down).
  • Plugin server-module bundling fixed + zero-warning build (2026-10-06): the template-literal import(\./${pluginId}/index.server`)in the plugin registry/init was not statically analyzable — rolldown globbed./*//index.server, matched nothing (empty-globwarning), and the modules silently 404'd in production builds (dev-only behavior): redirect-manager's migrations +beforeSavehooks and pagespeed'sssrHooknever loaded in prod. Fixed with a literalimport.meta.glob(”./*/index.server.ts”)in the server-onlyinit.server.ts, loaders registered into pluginServerRegistry, and all three registry lookup sites routed through it (migrations, ssrHook, activation); PluginServerModulenow typeshooks/migrations/ssrHookexplicitly. Also addedsrc/utils/v8-shim.tstopackage.json sideEffects — the Bun v8-module shim was being tree-shaken out of bundles (ignored-bare-importwarning), which would break bson/mongoose under Bun. Build is now warning-free and the plugin chunks are verifiably inbuild/server/chunks/`.
  • Stripe 22.6.2 → 23.0.0: no removed APIs touched; API version pin moved 2025-03-31.basil → 2026-09-30.endive (src/plugins/stripe/server/stripe.ts).
  • Index write-amplification A/B (2026-10-04, MariaDB + PostgreSQL, paired alternated arms + PK-read control, statistics equalized via ANALYZE): the legacy prefix-duplicate collection index ((tenantId, status, updatedAt) — pre-§3.41 policy shape, no longer created) is write-lane-neutral on both engines: update 1c −2.3 %/−1.9 %, update 8c +2.3 %/−5.3 %, create 1c −2.8 %/+2.1 % — every lane inside the control drift (28 % MariaDB / 11 % PostgreSQL). Two measurement artifacts were caught and corrected en route: stale planner statistics (CREATE INDEX recomputes them post-seed — made the legacy arm look +10–20 % faster) and an underpowered 8c sample (~19 ops/worker at 150 shared iterations). Verdict: the policy was right to stop creating the duplicate; keeping it costs nothing measurable on the lane, so no cleanup migration is justified by write throughput (WAL/disk hygiene remains the only reason to drop it). Harness: tests/benchmarks/index-write-amplification-ab.test.ts.
  • Pre-existing formatter crash repaired: {theme.customCss}/{loginBranding.customCss} inside <style> crashed oxfmt’s CSS parser (breaking bun run check); both now render via <svelte:element this={'style'}>.

Quality gates (2026-10-04): bun run check 0 errors / 0 warnings · bun run test:unit 572 files / 5 105 tests · bun run lint:tenant -- --full 934 files · bun run risk:audit 5/5. Benchmarks: competitive-workload-replica green with BENCH_SCALE_GUARD=1 on both PostgreSQL and MariaDB.

3.46 October 2026 — 4-Adapter Transaction Parity, Audit Micro-Batching & Projection Type-Preservation

  • Cross-adapter transaction parity across all 4 database engines (src/databases/*/transaction-module.ts): SQLite, PostgreSQL, MariaDB, and MongoDB share unified transaction semantics. Includes statement timeout propagation, transaction isolation level configuration (read-committed, repeatable-read, serializable), re-entrant transaction detection (re-using existing transactions or falling back gracefully to SAVEPOINTs/client sessions), and explicit rollback signal handling (ROLLBACK_TRANSACTION). Comprehensive parity test suite (tests/unit/databases/multi-adapter-transaction-parity.test.ts, 400 lines) validates identical contract compliance across all adapters.
  • Audit log micro-batching (src/services/security/audit-service.ts): Mutating operations no longer suffer latency penalties or lag by 5 seconds. Micro-batch window (25 ms flush timer or 50 items) flushes pending audit records synchronously when configured or in background batches, ensuring mutations remain sub-millisecond while maintaining tamper-evident audit ledger integrity.
  • MongoDB unordered bulk operations (src/databases/mongodb/): High-throughput write batching leverages { ordered: false } to prevent head-of-line blocking on batch inserts and updates (tests/unit/databases/mongo-unordered-bulk.test.ts).
  • Virtual field projection type-preservation & fuzz resilience (src/databases/core/sql-adapter-core.ts): When SELECT projections request virtual fields located in JSON data blobs, the adapter preserves the data column to maintain native typed JSON values (numbers, booleans, objects, arrays) rather than coercing them into stringified SQL text. Skips the data blob only when all requested fields are known physical schema columns. Pinned via property-based fuzz test suite (tests/unit/databases/virtual-field-projections.test.ts) covering randomized column subsets, deeply nested dot-paths (a.b.c.d), Unicode identifiers, and malformed projection payloads.
  • Micro-batch adversarial concurrency stress test (tests/unit/services/audit-log-microbatch.test.ts): Verified that 500 simultaneous service.log() calls across 10 concurrent worker promises execute with zero dropped entries, buffer memory staying strictly bounded under MAX_TOTAL_BUFFER = 200, and an uninterrupted cryptographic SHA-256 hash sequence across all 500 records.
  • Smart local 4-database matrix runner (scripts/run-local-db-matrix.ts, bun run test:matrix:local): Probes local TCP listener ports (SQLite, PostgreSQL 5432, MariaDB 3306, MongoDB 27017) to auto-detect active engines and orchestrates the integration contract suite (tests/integration/databases/contract.test.ts) with automated server boot/shutdown across all online adapters.
  • System monitor E2E dashboard validation (tests/e2e/routes/config/operations.spec.ts): Automated Playwright test verifying that /config/monitor renders service health tables, system CPU/memory cards, and cryptographic audit trail with zero browser console errors.
  • Live truth-latency re-verification (tests/benchmarks/truth-latency.test.ts on 2026-10-05): Warm HTTP End-to-End 0.262 ms (p95: 0.322 ms, 3,785 RPS); Cold HTTP 0.432 ms (p95: 0.587 ms, 2,304 RPS); Local SDK 0.005 ms (p95: 0.009 ms, 177,378 RPS). ACID transaction commit: 0.005 ms, 218,055 RPS.

Quality gates (2026-10-05): bun run check 0 errors / 0 warnings · bun run test:unit 577+ files / 5,150+ tests · bun run test:matrix:local 4/4 adapters passed · bun run lint:docs 263 files clean · bun run risk:audit 5/5 passed.

Storefront — European checkout (2026-10-05)

Catalog prices on /shop are gross. Checkout shows the trader, the VAT breakdown, and an order button that states the payment obligation (zahlungspflichtig bestellen when the store language is German). Guests withdraw at /widerruf. The order stores an invoice number, net, and tax. A price reduction shows the lowest price of the previous 30 days when that history exists. OSS returns, packaging registration, and WEEE stay with the merchant. Guide: E-Commerce Overview.

3.47 October 2026 — Pre-compiled Query Builder Templates & Sub-Millisecond SQL CRUD Acceleration

  • Shared SQL query builder unification (src/databases/core/sql-query-builder.ts): Unified the duplicated builder logic across SQLite, PostgreSQL, and MariaDB into a single SqlQueryBuilder<T> parameterized by SqlDialect. All engine-specific quoting, placeholder binding ($1 vs ?), JSON extraction, and ILIKE/LIKE operators are driven by dialect constants.
  • Compiled plan expansion: Extended the compiled prepared-statement cache beyond scalar equality to support whereIn, whereNotIn, whereBetween, whereNull, whereNotNull, orWhere, and parameterized pattern searches (search_or). Any dynamic JSON field filters, unmapped expressions, or non-bindable objects safely degrade to Drizzle dynamic AST with zero runtime errors.
  • Dialect-safe LIKE ESCAPE binding: MariaDB treats backslashes inside string literals as escape characters; binding the escape backslash as a query parameter (ESCAPE ? / ESCAPE $N) ensures cross-dialect compatibility without double-escaping bugs or syntax failures.
  • Standard CRUD read path acceleration (src/databases/core/sql-adapter-core.ts): Added pre-compiled template fast paths to findMany, findPage, and count. Pre-compiled query shapes execute directly via executeCompiled(), bypass Drizzle runtime AST building and SQL string assembly, and hoist schema date-conversion metadata so read lists stop re-resolving table column schemas per row.
  • Self-measured benchmark gains (2026-10-06):
    • PostgreSQL: QUERY BUILDER LIST (50) 4.88 ms → 0.75 ms (~6.5× faster, 202 → 1,204 RPS); FIND MANY (limit 50) 1.27 ms → 0.79 ms (−37 % latency, +40 % RPS); FIND PAGE (50 hasMore) 1.26 ms → 0.77 ms (−39 % latency, +61 % RPS); FIND MANY offset 50 1.09 ms → 0.55 ms (−49 % latency, 1,728 RPS).
    • MariaDB: FIND MANY (limit 50) dropped to 0.558 ms (1,694 RPS); QUERY BUILDER LIST (50) dropped to 0.545 ms (1,753 RPS); FIND PAGE (50 hasMore) dropped to 0.587 ms (1,684 RPS); COUNT dropped to 0.487 ms (2,020 RPS).
    • SQLite: Reads stay sub-100µs: FIND MANY (limit 50) 0.059–0.065 ms (14,000–15,600 RPS); COUNT 0.058 ms (16,231 RPS).
  • Documentation & test coverage: Reference documentation published at SQL Query Builder. Verified via 19 operator unit tests (tests/unit/databases/sql-query-builder-operators.test.ts), compile tests (sql-query-builder-compile.test.ts), and read-path tests (sql-query-builder-read.test.ts).
  • Keyset pagination onto the compiled plan (2026-10-06): mergeKeysetFilter (core/page-utils.ts) wraps a base query plus the (sortField, _id) cursor in $and / $or; the builder previously bailed on every top-level $ key except $or, so deep pages fell back to the dynamic AST. applyFilterAndOptions now flattens $and recursively and orWhere compiles OR-of-AND groups of physical leaves, and orWhere self-heals to Drizzle for nested $or, JSON fields and non-bindable operands — it never partially compiles an OR. The compiled path also coerces ISO strings / epoch numbers to Date for timestamp columns (shared coerceDateColumnValue, parity with mapQuery), so a compiled comparison can never bind TEXT against an INTEGER timestamp_ms column. Verified end-to-end by both keyset integrations (tests/integration/api/collections-keyset-cursor.test.ts, tests/integration/databases/find-page-count-contract.test.ts) and the operator/compile unit suites.

3.48 October 2026 — SQLite write tail: checkpointing moved off the request path by default, and group commit (opt-in) takes the 8c queue tail from 🔴 to 🟡

Two levers, measured separately on the same build (competitive-workload-replica, sqlite, warm 8c, production server, 300 samples; each verdict is the repo’s own 3-run ensemble median via bun run verify:benchmark-local).

Baseline (recorded pre-change run, both 🔴): create p50 4.41 / p99 24.14 ms (+19.7 ms excess), update p50 4.23 / p99 30.8 ms (+26.6 ms); the tail was entirely SQLite’s synchronous auto-checkpoint (§3.35).

Shipped default — off-request-path checkpointing. applyPragmas now defaults to PRAGMA wal_autocheckpoint=0 unless SQLITE_WAL_AUTOCHECKPOINT is set explicitly, and startWalHousekeeping runs startWalCheckpointScheduler by default (opt out SVELTY_WAL_CHECKPOINT=0) with a new WAL-size cap (SVELTY_WAL_MAX_BYTES, default 64 MB) beside the existing idle and 60 s-force rules. synchronous=NORMAL is unchanged — durability is not weakened. Result (3-run ensemble): create avg 4.17 / p50 4.0 / p95 5.41 / p99 10.48 / +6.477 ms 🔴, update avg 3.63 / p50 3.47 / p95 5.67 / p99 7.68 / +4.214 ms 🔴 — slowCount 0 on every write row (no sample above 10 ms; the discrete ~25 ms checkpoint spikes are gone), matching the earlier single-run A/B (AUTOCHECKPOINT=0 → slowCount 0, residual excess ≈ 6.5 ms). The absolute tail excess is still above the 4 ms WATCH bound: the checkpoint spikes were removed, the write-mutex queue is not.

Shipped default ON (2026-10-09) — group commit is the default write path. The batcher is now constructed by default (SVELTY_SQLITE_GROUP_COMMIT defaults to 1; =0 restores the per-write commit control lane). Paired A/B on the 10k-doc matrix replica (competitive-workload-replica, --db=sqlite): create @8c 1,833 → 2,892 RPS (+57.8 %) and update @8c 1,896 → 3,347 RPS (+76.6 %) with improved p95 (6.74 → 5.29 ms and 5.38 → 3.48 ms), mixed @8c +12.1 %, Seed Burst +8.0 %; sequential lanes neutral (update +1.7 %) except create −9.9 % (single-pair, within the lane’s CoV). The remaining acceptance gate (no 🔴 row, --strict-variance green on a ≥3-run ensemble) is tracked in the roadmap.

Opt-in (SVELTY_SQLITE_GROUP_COMMIT=1) — write coalescing (original measurement). New src/databases/sqlite/write-batcher.ts queues batchable single-statement writes and drains them on a bounded event-loop tick (setImmediate/queueMicrotask, never a delay timer) into one BEGIN IMMEDIATE … COMMIT; each op keeps a SAVEPOINT so one failure rolls back only itself, results are buffered and settled after COMMIT, and a lone write takes the runSingle (no group transaction) path. The adapter only constructs the batcher behind the flag; transaction-control/PRAGMA statements and active write spans (re-entrant/worker contexts) bypass it. Result (3-run ensemble): create avg 2.86 / p50 2.6 / p95 4.38 / p99 5.54 / +2.943 ms 🟡, update avg 2.81 / p50 2.72 / p95 4.07 / p99 4.95 / +2.225 ms 🟡, slowCount 0 — both rows out of 🔴, and p50 fell ~1.3 ms (4.0 → 2.6 create, 3.5 → 2.7 update), so the drain is genuinely coalescing writes rather than just adding a hop.

Residual floor, stated plainly: ≈ 2.2–2.9 ms p99 − p50, not ≤ 1 ms. Group commit only folds writes that reach the queue within the same tick; at 8c the requests are staggered by the HTTP/auth pipeline, so many drains still carry a single op, and the synchronous driver blocks the event loop for each batch’s execute phase — the queue is shortened, not flattened. §3.36’s commit-collapse falsification still holds for the per-statement commit itself; the win here is queue coalescing, not a cheaper commit. Kept opt-in as specified.

Verification: tests/unit/databases/write-batcher.test.ts (10 cases: batching, lone-write path, maxBatchSize split, per-op isolation, throw/unsettled safety net, default-scheduler deferral) and tests/unit/databases/wal-scheduling.test.ts (9 cases: size-cap band, interval gate, idle precedence, scheduler wiring). node ./node_modules/vitest/vitest.mjs run tests/unit/databases → 53 files / 461 passed.

  • Phase 4: True Statement Coalescing & Adaptive Window (2026-10): Replaced per-write TCP statement round trips with PostgreSQL array-based multi-row statement coalescing (statement-coalescer.ts, postgresql/adapter-core.ts):

    • INSERT Coalescing (default ON via SVELTY_WRITE_COALESCING): Batches concurrent single-row inserts for the same collection into INSERT INTO ... SELECT ... FROM UNNEST($1::text[], ...) RETURNING * with String(val) text-array coercion. Measured across alternated replica runs on PostgreSQL (100k docs, 8c): +12–18% create throughput (2,216 → 2,624 RPS) with improved p95 latency (4.05–4.21 ms vs 4.76–4.94 ms).
    • UPDATE Coalescing with Lever 2 Adaptive Window (SVELTY_WRITE_COALESCE_UPDATE=1, SVELTY_COALESCE_UPDATE_WINDOW_MS=1): Executes multi-row UPDATE ... FROM UNNEST(...) WHERE t._id = u._id with an adaptive 1 ms burst-hold window. While wave-sized batches (2–5 rows) previously regressed due to UNNEST join/cast overhead, the 1 ms window grows batches to 8–16 rows where the join amortizes, delivering +11–15% update throughput gain (update/create ratio 1.17–1.18 vs control 1.02–1.07) on the 100k-doc harness. Lone writes still flush on the tick boundary with 0 ms added hold delay. 2026-10-09: two paired A/Bs on the 10k-doc matrix replica measured −41–54 % on the update lanes with the lever defaulted, so it stays opt-in — the gain is harness-profile-dependent (batch-size dynamics at 10k docs never reach the amortizing 8–16-row waves).
    • Fault Isolation: Batch rejection or duplicate-key aborts automatically replay each row individually through its single-statement path — no sibling write is corrupted by an error. Verified: tests/unit/databases/statement-coalescer.test.ts (8/8 green), full unit suite 5,666/5,666 green.
  • Phase 5: Cold Read Lane Direct-to-Wire List Streaming (findListWireStream) (2026-10-09): Extended the Wire Plane architecture from point reads (findPointWireStream) to cold list reads (findListWireStream in handle-collection-read-lane.ts):

    • In-Engine Direct-to-Wire Aggregation: Eliminates V8 entity hydration, DTO transformations, and JSON.stringify allocations by producing the wire JSON array inside the database engine.
    • Fail-Closed Admission Predicate: Enforces strict admissibility via isWirePlaneAdmissible(event, wireMeta): requires published-only status, default pagination limit (50), page 1, no custom filter/sort, absence of mutating lifecycle hooks, and projection equivalence.
    • Strict Multi-Engine Paired A/B Measurement: Evaluated across paired alternating arms (Domain Plane vs Direct-to-Wire, 3 alternated pairs, 50-row pages):
      • SQLite (In-Process C Engine): +22.7% to +50.2% RPS (0.124 ms → 0.083 ms, −33.1% latency). Shipped and enabled.
      • PostgreSQL (Client-Server Network Engine): −29.9% RPS (0.852 ms → 1.215 ms, +42.6% latency). Server-side jsonb_agg serialization in the Postgres backend is slower than Node.js V8 binary-row parsing + C++ JSON.stringify. Declines list wire contract ({ kind: "declined" }), seamlessly preserving the faster Domain Plane.
      • MariaDB (Client-Server Network Engine): −16.0% RPS (0.873 ms → 1.046 ms, +19.8% latency). JSON_ARRAYAGG similarly incurs server-side serialization overhead. Declines list wire contract.
    • Verification: tests/unit/hooks/collection-read-lane.test.ts (22/22 green), full core unit suite (139 files, 1,416 tests green), tsc --noEmit clean, bun run check clean, bun run risk:audit 5/5 clean.
  • Phase 6: Release-Pipeline Hardening (version:verify, SBOM lockstep, artifact verification) (2026-10-10): Closed the failure modes that left v0.2.0 sitting untagged for days — the release job is gated on a fully green main CI, so a red run silently skips every release (14 consecutive skips traced to the plugin-glob/telemetry/RTL failures now fixed).

    • Manifest ↔ SBOM lockstep: scripts/version.ts now writes the CycloneDX root component (sbom.json metadata.component name/version/purl) in the same write as package.json, so a bump no longer depends on the pre-commit hook to avoid drift. bun run version:verify asserts that lockstep read-only and runs inside bun run check (pre-push Quality Gate + CI 03-check).
    • Release-workflow hardening (auto-release.yaml): a pre-flight version:verify, manual-version input validation, post-flight artifact verification (tag pushed + GitHub Release created — fails loudly otherwise), honest npm reporting (missing NPM_TOKEN → visible warning, rejected token → visible error; neither swallows the GitHub Release), and a $GITHUB_STEP_SUMMARY report. A workflow_dispatch run is the documented escape hatch when a persistent CI failure blocks the gate.
    • Verification: tests/unit/scripts/version-sbom.test.ts (SBOM rewrite preserves every unrelated byte, drift detection on name/version/purl, --verify CLI contract) plus the updated version-bump.test.ts CLI assertions; live bun run version:verify green on create-sveltycms@0.2.0, and v0.2.0 released end-to-end once main CI went green.

Related

achievementslogtechnicalcomparisondatabasesecurity
Was this page helpful?