Important
Live pass/fail counts = GitHub Actions on this branch or PR. Numbers in docs drift; CI does not.
This page is the only test-status dashboard — route maps, inventories, and deep strategy live in the linked docs below (no separate archive).
Dashboard updated: 2026-10-24 — share-link metadata race, route-gate coverage, duplicate-testid gate. Unit 734 files / 6,897 tests; component interaction 29 files / 158 tests; P0 coverage 75.41 % statements / 66.11 % branches / 78.45 % functions / 77.55 % lines (floors 70/60/72/72); route-behaviour 139/139 applicable cells across 56 routes; mutation 7/7 killed. bun run check includes check:routes (floor 99), lint:trivial-assertions (0 findings across 1,095 test files) and the new lint-duplicate-testids gate (a route may not pass a testid a child component already renders — the media-gallery wrapper that broke Playwright strict mode); the four-DB integration suite is green on sqlite/postgresql/mariadb/mongodb. Full 4-DB integration, Bench ×4 and the E2E matrix remain CI-only.
1. Snapshot
| Question |
Answer |
| Are the gates healthy? |
Design A++: pre-commit (format + type gate + unit + real-DOM components + risk:audit) → pre-push (CI unit parity: coverage + components + mutation; build 4 adapters; tenant; SQLite integration + DB parity when adapter code changed; path-mapped E2E + flake budget) → CI multi-DB + full E2E + Bench |
| Where to look first when red? |
Failing CI job name → matching hot area below or route control map |
| Biggest recent win |
Route-behaviour gate at 99/99 applicable cells + unfailable-assertion lint — the 18 new route suites pin authz, persistence and concurrency for dispatch, migration, upload, preview and builder paths; the plugin dispatcher’s silent 500 was found and fixed (2026-10-24) |
| Biggest open risk (product) |
P0 line coverage is still ~75 %, and the external theme/UX findings are behavioural/visual: no route-contract test can see double chrome, 28 px controls or a missing tabpanel. These need visual regression + a11y sweeps; deep forms outside the DOM suites still rely on CI shards. |
Coverage scope (what the percentages mean)
Two different numbers exist; never quote one as the other.
| Scope |
Lines |
Functions |
Branches |
Statements |
How measured |
| P0 gate (the enforced floor) |
77.55 |
78.45 |
66.11 |
75.41 |
bun run test:coverage (floors 72/72/60/70) |
| Merged unit ∪ DOM (best/file) |
35.32 |
34.60 |
30.89 |
35.07 |
bun run test:coverage:all (manual, no thresholds) |
Whole src/ (unit suite only) |
32.61 |
29.73 |
29.33 |
31.98 |
COVERAGE_ALL=1 bun run test:coverage |
Whole src/ — .ts logic only |
— |
— |
— |
44.2 |
same run, aggregated from coverage-final.json |
Whole src/ — .svelte files |
— |
— |
— |
1.3 |
unit config does not mount components (see DOM layer) |
Whole-src/ unit coverage by area (statements): hooks 71.8 %, utils 59.1 %, content 52.3 %, services 45.9 %, stores 40.5 %, databases 39.8 %, plugins 35.0 %, widgets 17.1 %, routes 14.1 %, components 7.8 %. The merged report lifts the UI layers (components 17.63 %, routes 19.83 %, widgets 17.68 % lines) because the DOM suite measures the Svelte client build; 61 files are measured better by the DOM report. The .svelte figure in the unit-only report is low by construction: that config forces SSR and the interaction suites run in vitest.dom.config.ts. The merge is best-of-both per file (never summed) because the two builds produce incompatible statement maps. Integration and E2E coverage are still not merged, so this is a floor for unit + component layers, not full product coverage.
Note
The P0 figure is the only number with an enforced threshold. The whole-src/ figure is a measurement (2026-10-18), not a gate — quoting it as “CMS coverage” would understate the DOM/E2E layers and quoting the P0 figure as “CMS coverage” would overstate it.
Branch-red → green: MariaDB bulk, E2E CSRF, route breadth (2026-10-18)
|
|
| Status |
Fixed and verified locally — the three failing CI jobs on next are addressed; CI re-run still required. |
| MariaDB |
batch.bulkUpdate silently updated zero rows on MariaDB. prepareUpdateValues (isUpdate=true) omits the PK, but both MariaDB bulk strategies read the row id from those values — the JSON_TABLE JOIN and the CASE ladder matched String(undefined). The prepared row now carries the id (the SET-column loop still excludes it). Contract suite: 4 fail → 20/20. |
| E2E CSRF |
Media-folder RBAC returned 403 “Cross-site DELETE form submissions are forbidden”. The login helper discarded the CSRF cookie (pairs.slice(0, 1)), and the probe spec sent no X-CSRF-Token/Origin. applySessionCookie now keeps every cookie (session stays httpOnly) and primes the context; the spec sends both headers. 1/1 locally. |
| E2E users |
Management delete failed because the developer row was not found after the 15 s wait. force clicks are gone; the row/confirm path now uses the real pointer and the shared modal-confirm testid. 1/1 locally (47.1 s). |
| Server-side |
requiredIf was enforced only on an explicit publish transition. Updating an already-published entry without sending status could blank a required field. The update path now reads the stored status when a required field is touched and validates the merged row; drafts remain allowed to be incomplete. +3 unit tests (required, conditional, draft-allowed). |
| Route breadth |
New tests/e2e/routes/config/route-breadth.spec.ts: 18 static admin routes must return HTTP < 400, stay off /login, render a visible shell, and contain no error-page marker. 18/18 in 18.9 s. |
| Smoke |
After a fresh build the 7 ERR_TOO_MANY_REDIRECTS console errors did not reproduce; only the intended / → /en/collection/pages 302 appears. The smoke now logs every 3xx chain, and the runner verifies the port is free, fails fast if the preview dies, and clears stale Playwright artifacts before launch. |
| Coverage |
Added bun run test:coverage:all — full-source unit report + DOM report, unioned best-measurement-per-file (scripts/merge-coverage.ts, never summed because the builds’ statement maps differ). Result: 35.32 % lines / 34.60 % functions / 30.89 % branches / 35.07 % statements; components 17.63 % and routes 19.83 % (up from 7.8 % / 14.1 % unit-only). |
| Verification |
bun run check 0 errors / 0 warnings; bun run test:unit 660 / 6,188; bun run test:unit:components 20 / 122; MariaDB bulk contract 20/20; smoke 5/5; management E2E 1/1; media RBAC 1/1; route breadth 18/18; bun run lint:docs clean. |
2. Health by layer
| Layer |
Confidence |
What it proves |
How to run |
| Unit (Vitest 5) |
A++ |
660 files / 6,188 tests in ~37–60 s — auth, lanes/turbo cache, CacheService, page gates, widgets, security, field write guards, compilation v6 + schema loader, build-parity guardrails, layout-id guard, real 2FA service, security middleware, submission replay guard, SSO/OAuth, plus SSRF-guard allowlist semantics and the server-side published-entry requiredIf guard. |
bun run test:unit · coverage: bun run test:coverage |
| Component interaction (Vitest + jsdom) |
A |
20 files / 122 tests — real Svelte 5 mount(): click dispatch, disabled/loading guards, Checkbox/Toggle keyboard + ARIA, Input/Select/Textarea events, Modal open/Escape/backdrop, ModalPrompt confirm/cancel, Tabs keyboard navigation, dropdown menus, confirm dialogs, SmartTable saved views, row selection/select-all/sort, TableIcons, the user bulk Multibutton, System Settings (load, dirty state, toggle, native + custom validation, save payload, discard, reset), Access Management role modal, Workflow editor, Webhooks create/edit/test/delete, Media Gallery virtual folders (create/nest/rename/RBAC delete), and the Collection Builder field editor + live preview (widget discovery/search, label→column generation, required/translated, widget-specific + nested-group settings, validation presets/regex/bounds, conditional visibility (showIf) & requirements (requiredIf), permissions matrix, delete/duplicate, drag-drop reorder, GuiFields serialization, and live form preview responsive viewports). Uses the client build; SSR-only assertions cannot see this wiring. |
bun run test:unit:components |
| Security regression |
A++ |
Hooks + RBAC defense-in-depth + full risk:audit (security-risk, secrets, slop, OSV) on pre-commit |
bun run test:security · bun run risk:audit |
| Integration (black-box) |
A++ |
80 test files; SQLite locally, 4-engine contract bundle in CI; local parity runner exercises the same contract suites on every reachable engine (bun run test:parity); bulk/atomic contract 20/20 (2026-10-18); zero-mock OIDC live suite (11) drives real discovery, /authorize 302, PKCE exchange, JWKS verification, and back-channel logout over loopback; lane headers + session page-load canaries. Counts drift — CI is truth. |
bun run test:integration (SQLite) · bun run test:parity (local engines) |
| E2E (Playwright) |
A− |
53 spec files — browser goldens/P0 journeys + smoke.spec.ts (7 tests: auth + hydrated shell, media toolbar hit-testing + advanced-search modal, virtual folder create→reload→delete, media metadata PATCH→reload, user identity, collection CRUD create→reload→API re-read→delete, health; no conditional assertions, pageerror guard, guaranteed cleanup) + admin route breadth (18 static routes: HTTP < 400, non-login URL, visible shell, no error page). Smoke runs in pre-push; full matrix in CI. User-management forces removed: the spec now proves the bulk menu and confirm dialogs are clickable by a real pointer. Host-contention flakes remain possible; CI is the arbiter and the flake budget gates retried-pass counts. |
bun run test:e2e · bun run test:e2e:smoke · bun run flake:budget |
| Benchmarks |
Selective |
The core suite runs on every CI push/PR with a regression gate (absolute budgets always fail; relative drift warns at ≥25 % + ≥0.25 ms, fails at ≥100 % + ≥1.5 ms on main/merge_group (warns on next/PRs)). The full matrix is long — not every PR. 4-DB spot re-check (self-measured 2026-09-19 — SveltyCMS self-assessment, not third-party verified; --only groups, sqlite/postgresql/mariadb/mongodb, production parity): 72 core-CRUD scenarios across the 4 engines + auth-session-AMR showed no regressions in the measured scenario set (deltas inside the recorded ±3–20% run-to-run spread). The one flagged outlier, cache-hit-ratio Cache Invalidation 0.595 → 1.089 ms, was a baseline mode mismatch, not a cache regression: the 0.595 ms record came from a standalone run on a NODE_ENV=development + TEST_MODE=true server (rate-limit/WAF/security pipeline bypassed), while matrix runs use production parity — the same build re-measured in the baseline’s own mode (TEST_MODE=true) is 0.538 ms. The cache layer’s own share of that ~1.1 ms request is ~2 µs (clearByTags 515 µs across 178 calls; ≤7 tags/call; ≤8 L1 keys). Comparison baselines must be recorded in the same execution mode as the run they judge — see benchmark-matrix. |
See benchmarks |
| Benchmark scope |
Partial |
Validated by the matrix (self-measured 2026-09-19, SveltyCMS self-assessment): core CRUD (4 engines), auth-session AMR write/read cost, cache hit/miss/invalidation at the cache layer, sorted/filtered 25k content queries, api-latency GET. Not yet covered by the matrix as of 2026-09-19: zstd-async compression, dispatcher request-body limiter, security-scan dedup, media-gallery composite indexes. |
See benchmark-matrix |
| Static / type |
A |
bun run check ends with bun run typecheck (svelte-kit sync → tsc --noEmit → check-svelte-diagnostics.ts, which gates errors and warnings); project-wide result on 2026-10-23 is 0 errors / 0 warnings. CI 03-check runs the same typecheck group. |
bun run typecheck · bun run check |
3. Testing tiers
| Tier |
Command |
Time |
Coverage (approx., drifts — trust CI) |
| Pre-commit |
git commit / hooks |
~2min |
Format + type gate + unit (6,188, ~37–60 s) + real-DOM components (122, ~13 s) + risk:audit + SBOM if needed |
| Pre-push |
bun run gate |
~6–12min |
Build 4 adapters + backdoor/bundle gates + tenant + SQLite integration + DB parity (adapter changes) + Chromium smoke |
| Pre-CI (local) |
bun run test:parity · bun run test:matrix |
~2–15min |
Contract suites on every reachable engine; --docker boots missing engines, --require-all fails on skips |
| CI |
GitHub Actions |
~8–12min |
Whitebox (policy + unit) + build + db-tests matrix + E2E + flake budget + benchmarks + performance gate |
Gates (local → CI) — source of truth: .githooks/pre-commit, .githooks/pre-push, .github/workflows/ci.yml
| When |
What runs (summary) |
| Pre-commit |
1. DB safety 2. Format + lint + typecheck (check) 3. Lint-staged 4. risk:audit 5. Unit 5b. Component interaction 6. SBOM sync (if lock/package changed) |
| Pre-push |
1. Build (COMPILE_ALL_ADAPTERS=true) 2. Backdoor verify (bench) 3. Bundle size 4. check (incl. typecheck) 5. Release readiness 6. CVE audit (info) 7. Secret scan 8. test:tenant 9. Deploy probe 10. SQLite integration 10b. DB parity (adapter changes) 11. Chromium E2E smoke |
| Manual |
bun run test:doctor · bun run test:security · bun run test:coverage · bun run test:parity · bun run flake:budget · bun run test:e2e · bun run test:e2e:smoke |
| CI |
Whitebox (policy + unit) → build → db-tests (4 DBs) → E2E prep + groups → benchmarks + performance gate |
CI smart reporting (job Summary tab + annotations):
| Job |
Reporter |
| E2E prep / E2E shards |
scripts/ci/ci-report-playwright.ts — pass/fail counts, first error, fix hints, preview.log tail |
| E2E prep / E2E shards (gate) |
scripts/check/check-flake-budget.ts — fails when retried-pass count/rate exceeds the budget |
bun test tests/integration/ |
bun test native output — per-file ✓/×, summary, no custom JSON |
| 📊 Bench + gate |
scripts/benchmark-matrix/ci-regression-gate.ts — budget/Δ table, ::warning/::error annotations, baseline progress |
| All Green |
scripts/ci/ci-report-all-green.ts — job matrix + fix order |
Details: git-workflow.mdx
4. Hot areas (recent)
CI performance gate + leaner whitebox matrix (2026-10-20)
|
|
| Status |
Shipped — runs #1800–#1802 and #1804 are green; #1803 proved the gate fires (MariaDB hooks +152 % vs baseline) and exposed single-run noise, which the confirmation guard now absorbs. |
| Gate |
scripts/benchmark-matrix/ci-regression-gate.ts turns the core benchmark suite into a merge signal. Absolute PERFORMANCE_BUDGET / ADAPTER_BUDGET_OVERRIDES breaches always fail; relative drift warns at ≥25 % + ≥0.25 ms and is severe at ≥100 % + ≥1.5 ms. On main/merge_group, a severe drift must repeat on the next run before it fails: the first observation annotates pending confirmation (1/2) and freezes the baseline so one noisy runner cannot re-baseline itself; next and PRs warn only. |
| Baseline |
tests/benchmarks/results is gitignored, so the per-adapter gate-baseline-<db>.json travels through actions/cache (bench-baseline-<os>-<db>-<branch>-…). The gate compares against the median of the last 7 samples; two prior samples are required before judging (building (1/2)), so the third run is the first to report a Δ. A recorded runMode/serverMode mismatch discards the series instead of comparing incompatible environments. |
| Notifications |
Every flagged metric emits a ::warning/::error annotation and a $GITHUB_STEP_SUMMARY row; bench-summary and all-green aggregate the gate table; benchmark-gate-<db> artifacts keep the JSON reports for 14 days. |
| Every push |
bench-core has no if gate: 📊 Bench + gate (<db>) runs on every push, PR, and workflow_dispatch. |
| Leaner matrix |
Five whitebox runners merged into two: 01-policy (format, lint, audit, secret scan, hardware tiers, lockfile, seams, version, typecheck, tenant, deploy-backdoor) and 04-unit. Job allocations 25 → 21; setup 1m08s → 52s; bootstrap 1m27s → 1m10s. |
| Cache key |
setup-sveltycms hashes bun.lock, bunfig.toml, .npmrc, and patches/** instead of package.json, so script-only edits no longer cold-install. |
| Tests |
tests/unit/benchmarks/ci-regression-gate.test.ts pins the threshold policy plus the filesystem sequence (run 1 writes the baseline, run 2 builds, run 3 compares), the history/exportMetric dedupe, the mode reset, and the confirm-then-freeze flow. 14/14. |
Security-core coverage + System Settings form (2026-10-18)
|
|
| Status |
Shipped — the 0 % security/auth files now run their real logic, and the System Settings form has a real DOM suite. |
| Unit |
New suites: two-factor-auth-service (18), handle-security (22), submission-guard (11), sso-session (40), google-auth (13), regexValueToLikePattern (5), allowTestEgressOrigin (3), isFieldVisible/isFieldRequired (2), plus the server-side published-entry requiredIf guard (3). Unit suite 660 files / 6,188 tests. |
| DOM |
System Settings (11), Access Management role modal (8), Workflow editor (6), Webhooks (10), Media Gallery virtual folders (6), Collection Builder field editor (16), Collection Builder validation/conditions/permissions/preview (8), and the entry Fields mount (4: showIf visibility, requiredIf indicator/error, stale-error cleanup, nested conditions) interaction suites. Component layer 20 files / 122 tests. |
| Integration |
Zero-mock OIDC (oidc-live, 11): a real in-process provider (RSA-2048, real HTTP) exercises discovery + caching, a real /authorize 302 → /token PKCE exchange, JWKS RS256 verification + cache reuse, fail-closed PKCE/replay/secret paths, discovery fallback, and back-channel logout (signature + iat/aud/nonce claims). No vi.mock of safeFetch/validateEgressUrl; the only seam is allowTestEgressOrigin(), an exact-origin allowlist that throws outside TEST_MODE. Integration suite 80 files. |
| Coverage |
Lines 67.20 → 75.73 %, statements 65.48 → 73.92 %, functions 70.46 → 75.71 %, branches 57.34 → 64.33 % (P0 set). File deltas: 2FA 0.63 → 86.71 %, handle-security 0 → 87.22 %, submission guard 0 → 100 %, Google OAuth 0 → 88.61 %, SSO session 33.33 → 93.17 %. Floors raised to 72/72/60/70. |
| Bugs found |
1) Input type="number" bound strings (dynamic type skipped Svelte coercion) → min/max validation bypassed and numbers saved as strings; 2) SSO wildcard allowedRedirectUris never matched (* was not escaped before the \* replacement); 3) RP-initiated logout built the URL from provider.endSessionEndpoint instead of the discovered endpoint (undefined?...); 4) malformed percent-encoded OAuth state threw instead of returning null; 5) back-channel logout accepted any provider-signed token — iat freshness (±300 s), aud↔clientId, and the spec’s nonce-forbidden rule are now enforced (found by the zero-mock OIDC suite); 6) Collection Builder delete/duplicate actions were merged back into the field (__delete/__duplicate were never handled), so Delete kept the field and Duplicate overwrote it; 7) Widget settings with a group entry rendered an empty fieldset (InputSwitch never forwarded schema.fields); 8) the widget-picker search filter was non-reactive ({const} block declarations instead of {@const}); 9) the Validation tab ignored the canonical validation object and clearing a pattern left stale rules behind — cloneValue now hydrates pattern/patternMessage/minLength/maxLength/min/max from validation, and handleSave serializes and cleans both shapes; 10) showIf/requiredIf were preview-only — the live entry form (collection-display/fields.svelte) rendered every field; it now filters visibility and computes effective requiredness through the shared isFieldVisible/isFieldRequired helpers, passes the requirement to WidgetLoader, and clears stale required errors when a field hides; 11) create-entry page crashed with a 500 (effect_update_depth_exceeded → ERR_TOO_MANY_REDIRECTS) — filteredFields depended on currentCollectionValue and recreated field objects per keystroke, so WidgetLoader effects re-ran validation → onFieldSync → store update → new field objects. Fixed by keeping field identity stable, evaluating visibility in the template, memoizing requiredOverride inside WidgetLoader, and revalidating requiredness without onFieldSync. |
| Remaining |
Deep route-level forms outside the covered System Settings / Access Management / Workflow / Webhooks / Media Gallery / Collection Builder field-editor surfaces still rely on CI shards. showIf/requiredIf are now evaluated by the live entry form through shared helpers (unit-tested), but there is no dedicated DOM mount of collection-display/fields.svelte yet. The OIDC suite uses a local provider, not a cloud IdP tenant. |
Create-entry 500 — reactive loop fixed (2026-10-18)
|
|
| Status |
Shipped — the /en/collection/<id>?create=true 500 is gone. |
| Symptom |
A seeded minimal collection opened the create view as 500 — System Error, then ERR_TOO_MANY_REDIRECTS. The server logged no [GlobalError]; the failure was client-side. |
| Root cause |
fields.svelte made filteredFields depend on currentCollectionValue and rebuilt field objects on every keystroke. WidgetLoader effects depend on field, so each keystroke re-ran validation → onFieldSync() → store update → new field objects → effect_update_depth_exceeded. |
| Fix |
Stable filteredFields; visibility evaluated in the template; WidgetLoader gets a memoized requiredOverride; a dedicated requiredness-revalidation effect does not call onFieldSync(); hidden fields clear stale required errors. |
| Verification |
Fresh COMPILE_ALL_ADAPTERS=true bun run build + clean smoke: 5/5 passed (6.0 s browser phase); bun run check 0/0; bun run test:unit 660 / 6,188; bun run risk:audit 6/6. The smoke now logs pageerror, console errors and 3xx redirect chains, so a future 500/loop reports the real URL instead of an opaque browser error. |
| Residual |
ERR_TOO_MANY_REDIRECTS console errors still appear during the collection page (tests pass) — likely a media/avatar/asset request; triage separately. |
Spotless-testing pass: type gate, composite DOM, DB parity, flake budget (2026-10-18)
|
|
| Status |
Shipped — the six reliability gaps from the testing review are closed or explicitly gated. |
| Type |
bun run check now ends with bun run typecheck (svelte-kit sync → tsc --noEmit → svelte-check); CI 03-check runs the same group. Project-wide 0 errors / 0 warnings after fixing 31 tsc + 42 svelte-check errors. |
| DOM |
Composite suites added for Tabs, Dropdown, ConfirmDialog, SmartTable saved views, row selection/select-all/sort, TableIcons, the user bulk Multibutton, System Settings, Access Management, and Workflows. Component layer: 15 files / 78 tests. |
| Security |
New unit suites: real TwoFactorAuthService (18), handle-security middleware (22), submission replay guard (11), SSO/OIDC (39: discovery, JWKS RSA/HMAC, exchange, front/back-channel logout), Google/GitHub OAuth state (13), plus the $regex LIKE translation (5). Found/fixed: dynamic Input type="number" stored strings, SSO wildcard allowlist never matched, RP-initiated logout used undefined for a discovered endpoint, malformed percent-encoded OAuth state threw, Workflow inspector bind:checked={undefined} crashed, HMAC tokens hit JWKS discovery before symmetric verification. |
| Admin forms |
New jsdom suites: System Settings (11), Access Management role modal (8), and Workflow editor (6). |
| E2E |
Removed force: true from user-management checkbox/confirm clicks, permission checkboxes, the profile appearance link, and redirect delete. management.spec.ts 3/3 passes with real pointer clicks; auth-toggle checkboxes now click their visible labels (tests/e2e/helpers/checkbox.ts) instead of evaluate(el.click()). The CI-parity runner now defaults to one worker (shared SQLite), matching CI shards and removing cross-file reset/seed flakes. |
| DB |
bun run test:parity (scripts/runs/run-local-db-matrix.ts) runs the adapter contract suites on every reachable engine, with --docker, --only, --build, and --require-all. Pre-push runs the core contract when src/databases/ changed. Verified green on all four engines on 2026-10-18. |
| Coverage |
P0 floors raised from 48/47/40/47 to 72/72/60/70 (lines/functions/branches/statements) against measured 75.69/75.71/64.30/73.88; audited files: 2FA 86.71 %, security middleware 87.22 %, replay guard 100 %, Google OAuth 88.61 %, SSO/OIDC 93.17 %. |
| Flake |
bun run flake:budget enforces ≤2 retried-pass tests and ≤3% flake rate per E2E shard (0 for the wizard/auth-prep shard); CI runs it after every Smart Playwright summary. Local Playwright now writes tests/playwright-results.json too; PLAYWRIGHT_RETRIES=1 makes local flakes visible. |
| Validate |
bun run check · bun run test:unit (660 / 6,177) · bun run test:unit:components (15 / 78) · bun run test:coverage · bun run test:parity · management E2E 3/3 · bun run lint:tenant -- --full · bun run lint:docs. |
Real-DOM component layer + pre-push smoke gate (2026-10-18)
|
|
| Status |
Shipped — the missing middle between mocked unit tests and slow Playwright is now covered. vitest.dom.config.ts mounts real Svelte 5 client components in jsdom; tests/unit/components/dom/ covers Button, Checkbox, Toggle, Input/Select/Textarea, Modal, and ModalPrompt. Pre-push now runs a 7-test Chromium smoke gate that mirrors the full E2E journeys. |
| Why |
SSR-only assertions could not see click wiring, disabled/loading guards, keyboard activation, ARIA normalization, or modal confirm/cancel paths. E2E was the only layer that clicked anything. |
| Unit |
bun run test:unit — 655 files / 6,065 tests (~67 s). Component suite: 6 files / 33 tests (~1.9 s). |
| E2E |
tests/e2e/smoke.spec.ts — 7 tests (~6 s browser phase): auth + hydrated shell, media toolbar geometry/hit-testing + advanced-search modal, folder create→reload→delete, media metadata PATCH→reload, user identity, collection CRUD create→reload→API re-read→delete, health. Shared helpers live in tests/e2e/helpers/media.ts; any uncaught pageerror fails the test. Wired into pre-push and CI 04-unit runs the component suite. |
| Fixed |
CSRF cookie was dropped by applySessionCookie (folder-RBAC 403s); ModalPrompt had no deterministic testids; test:e2e:smoke could test a stale bundle (now detects newer source); vite.config.ts monkey-patched node:fs, breaking config re-bundles. |
| Docs |
ADR · this page · AGENTS.md command table. |
Browser reality & composite editorial journeys (2026-10-23)
|
|
| Status |
Shipped — browser-side product stability improvements: (1) widget value synchronization in widget-loader.svelte provides an $O(1)$ primitive fast-path (bypassing serialization for keystrokes in inputs) and cycle-safe WeakSet serialization for complex objects so rich text, relations, and objects notify onFieldSync without sub-2ms budget degradation or circular errors; (2) silent save abort removed from header-edit.svelte, with a non-mutating toast.info('No changes to save') exit on unchanged edits to prevent phantom DB revisions; (3) validation failure identifies human-readable field labels instead of raw DB keys, auto-activates Tab 0 if inactive, and reliably moves DOM focus to the failing field via CSS-escaped selector resolution; (4) zero-error guard expanded with same-origin HTTP $\ge 500$ response listening. |
| Gate |
Pre-push Step 8 automatically expands from smoke to include the Kitchen Sink journey when widgets or collection display code changes. New test:e2e:editorial and benchmark:browser npm scripts. |
| Component |
New suite: tests/unit/components/dom/widget-inputs-interaction.test.ts (client DOM mounting, two-way value bindings, ARIA attributes, and validationStore interaction). |
| E2E |
New suites: tests/e2e/routes/collection-builder/kitchen-sink.spec.ts (multi-widget editorial lifecycle), tests/e2e/routes/admin/editor-boundary.spec.ts (non-admin editor experience and boundary protection), tests/e2e/browser-vitality.spec.ts (Navigation Timing, typing responsiveness, heap memory stability in Chromium). Attached to tests/e2e/helpers/zero-error-guard.ts. |
| Docs |
adr-testing-2026 · e2e-coverage-matrix · e2e-testing-guide · benchmark-matrix · widget-test-coverage · rbac-testing · test-to-docs-map |
Seam coverage + admin route→test gate (2026-10-16)
|
|
| Status |
Shipped — three seam principles close the regressions that had escaped all three test layers (their tests now pass). Identifier parity: the token list exposes the hashed token value but lookup re-hashes, so it could never resolve — lookup now resolves by row _id (src/routes/api/[...path]/handlers/tokens.ts). Persistence assertions: token delete now fails loudly on deletedCount === 0 instead of silently no-opping. Failure paths: token validity edits sent expiresInHours (not a column) — the UI now sends a concrete ISO expires (src/routes/(app)/user/components/multibutton.svelte, modal-edit-token.svelte); collection edit/delete persistence, sign-up invite-token recovery, entry-list search typing, and the admin error boundary (“page moved” now 404-only; recovery buttons variant="outline") were fixed via the new src/utils/error-display.ts. |
| Gate |
New enforcement gate tests/route-test-coverage.ts (APP_ROUTE_COVERAGE manifest + verifyAppRouteCoverage()) with tests/unit/route-test-coverage.test.ts, running in bun run test:unit. Enforces bidirectional route↔test parity across all admin (app) route groups (inventory parity + coverage parity). |
| Unit |
New suites: tests/unit/routes/token-routes-handler.test.ts, tests/unit/utils/error-display.test.ts, tests/unit/route-test-coverage.test.ts. |
| Integration |
New suites: tests/integration/routes/collection-page-load.test.ts, tests/integration/api/token.test.ts. |
| E2E |
New suites: tests/e2e/routes/collection-builder/content-entries.spec.ts, tests/e2e/routes/user/token-actions.spec.ts, tests/e2e/routes/login/extended-auth.spec.ts. |
| Docs |
three-layer-completeness · e2e-coverage-matrix |
4-Adapter Transaction Parity, UNNEST/JSON_TABLE Engine & Micro-Batching (2026-10-05)
|
|
| Status |
Shipped — 4-adapter cross-engine transaction parity across SQLite, PostgreSQL, MariaDB, and MongoDB. Unified handling for isolation levels, statement timeouts, re-entrant/nested transactions, and clean rollback signal propagation (ROLLBACK_TRANSACTION). PostgreSQL UNNEST array batching (insertMany, rawBulkUpdate) and MariaDB JSON_TABLE bulk updates with utf8mb4_bin collation safety shipped (~19× speedup on 100 heterogeneous updates, 68.4 ms → 3.6 ms). Audit log micro-batching (25ms window, 50-item buffer) eliminates synchronous mutation stalls while preventing unbounded lag. Field projections safely exclude data JSON blobs when all requested fields are physical columns while preserving native typed JSON values for virtual properties. Fast-lane payload limiter (256 KB) enforces HTTP 413 before processing. |
| Tests |
Unit 577+ files / 5,150+ tests green in ~20 s. New suites: multi-adapter-transaction-parity.test.ts (400 lines testing rollback/timeout/isolation parity across all 4 adapters), mongo-unordered-bulk.test.ts, monitoring-diagnostics.test.ts, virtual-field-projections.test.ts, and audit-log-microbatch.test.ts. Full risk:audit (5/5 checks passed), bun run check (0 errors / 0 warnings), bun run test:security (19 files / 345 tests passed). |
| Security |
Zero-trust 4-layer defense in depth respected: WAF & fast-lane payload size limiting at Layer 1; ENDPOINT_PERMISSIONS fail-closed mapping at Layer 2; mutation admin verification at Layer 3; RBAC and collection guards at Layer 4. HMAC-SHA-256 API key security and CSPRNG session tokens preserved. |
| Perf |
Truth-latency verified on live host (2026-10-05): warm HTTP end-to-end 0.262 ms (p95 0.322 ms), 3,785 RPS; cold cache-busted 0.432 ms (p95 0.587 ms), 2,304 RPS; Local SDK 0.005 ms, 177,378 RPS. ACID transaction commits: 0.005 ms, 218,055 RPS. MariaDB bulk updates: 68.4 → 3.6 ms. |
| Docs |
achievements-2026 · performance-architecture · api-security · benchmark_sqlite · benchmark_postgresql · benchmark_mariadb |
| Open |
Wire no-status-column branch needs dedicated integration test case; dedicated CI job for MULTI_TENANT=true browser isolation; contention-free local E2E harness flags. |
Two-Plane read-lane hardening, diagnostics to zero & widget-suite consolidation (2026-09-27)
|
|
| Status |
Shipped — the fast read lane now mirrors the pipeline it skips. Publication clamp: 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 before serialising. The clamp itself was repaired — loadOneById compared the filter name (published) against the stored value (publish) and denied every status-bearing row to a clamped caller; it now uses isPublishedStatus(). Lane gates (all before any cache lookup): endpoint parity (collections:read only, matching ENDPOINT_PERMISSIONS), isLaneFlacExempt() (declines when a FIELD_PERMISSIONS policy or readRoles-guarded field applies, fails closed on error), and isPermissionBitsetStale(permRev) so a role edit reaches lane traffic instead of serving old grants for the whole sliding TTL. One transport: kernel-dispatcher.server.ts (second response path with its own ETag/304 semantics and per-request new Request/new URL) deleted with its 11 unit tests. Diagnostics 42 → 0 by fixing the DatabaseResult producers (missing error on the failure branch) instead of casting. Widget suites: the 16 near-duplicate suites under tests/unit/widgets/custom/ were ported into the co-located src/widgets/custom/*/tests/ homes (documented portable intent) after a per-pair case-name + assertion-count check — 16 files / 72 tests removed with no lost coverage. PG createModel: the documented jsonIndex: false opt-out read the schema module instead of the collection definition, so it could never apply (always-true guard, plus a build warning); it now reads the collection config. Six product bugs found and fixed in the same pass: media uploads were scoped to a phantom tenant with the folder dropped (saveMedia’s tenantId/_basePath slots), the marketplace install/license handlers treated fetchApi’s envelope as a Response (so both flows could only fail), a /config/design-system $effect reverted every local edit before saving, avatar writes left the 15 s layout snapshot stale so the delete button never rendered, every server-rendered DOM id drifted from its hydrated twin (initIdGenerator() was never called by the app, so the UI kit’s generated ids fell back to a process-global counter and label[for]/aria-describedby linkage broke after hydration), and the layout snapshot’s invalidator was fire-and-forget so a save→reload could still read the old value (now awaited, and the Auth facade invalidates too — covering the HTTP route, SDK namespace and every internal writer). svelte-check 16 errors → 0 across 6 785 files — no gate ran a type check; two of the 16 were those bugs. Benchmark harness: etag-hash’s size-blind superiority claim replaced by the measured crossover, the DX probes no longer inherit the matrix’s 1 GB NODE_OPTIONS cap (which aborted svelte-check at ~12 s), and report formatting is awaited and also covers index.mdx. |
| Tests |
Unit 552 files / 4894 tests in 18.8 s (was 567 / 4 963; +3 for the new root-layout id-generator guard); SQLite integration 719 tests / 76 files (694 pass, 25 skip); bun run check, lint:docs (263 files) and risk:audit clean; project diagnostics 0/0. New helpers: tests/integration/databases/wire-contract.ts (requireWireMethods() fails loudly when an adapter lacks the Direct-to-Wire contract, unwrapResult() narrows the union) — removes 35 untyped accesses in the two parity suites. The namespace-ownership guard caught a registry path left stale by the widget-suite move (fixed in the same pass). |
| Security |
Five gates now stand between a warm session and raw wire bytes (publication clamp, endpoint parity, FLAC exemption, grant epoch, tenant resolution); 3 leak/bypass paths closed; the fail-closed no-status branch replaces a silent unfiltered read. |
| Perf |
Truth-latency ledger re-verified with two same-code runs (BENCHMARK_RECORD=1 … --only=truth-latency, SQLite) against the recorded 2026-09-26 baseline: warm HTTP end-to-end 0.911 → 0.486/0.393 ms, p95 1.122 → 0.620/0.481 ms, RPS 1 093 → 2 042/2 527; cold (cache-busted) 1.290 → 0.508/0.458 ms, RPS 772 → 1 957/2 171. Two runs of identical code differ by up to 24 %, so attribution needs the queued interleaved A/B. Ceiling position (raw-db-ceiling.ts --db=sqlite --scale=10000, raw HTTP floor): findById 32 %, listPlain 59 %, create 21 %, update 20 %. The benchmark reporter’s ranked summary printed improvements as 🔴 Top Regressions (sign bug) — now split into Top Regressions (positive deltas only) and Top Improvements. |
| Docs |
two-plane-core · server-hooks — Collection Read Lane · 4-Cell Benchmark Contract · widget-test-coverage · achievements-2026 |
| Open |
Wire no-status-column branch has no integration case (every provisioned table carries status); svelte-check is not part of any gate (16 errors lived in the tree until the DX benchmark ran it — now 0/0 over 6 785 files, but the drift class can return); the parallel Feature HTTP Read benchmark group trips under full-run contention (mixed-workload reliability, relational-performance consecutive errors) while staying green in isolation; all six product bugs behind the local E2E failures are fixed and verified (their interactions were hardened with outcome-verified retries: the design-system density select, the Edit User Details dialog, and the prep-phase sign-in submit); the list-stream reader (findListWireStream) was deleted as dead code (zero call sites) — a compiled list contract stays deferred; interleaved benchmark A/B still queued. |
Automated bottleneck-detection: heap stability, pool contention, P95/P99 variance (2026-09-26)
|
|
| Status |
Shipped — three automated regression suites making previously-invisible performance bottlenecks machine-detectable: (1) tests/unit/benchmarks/heap-stability.test.ts (3 tests): burst transformation heap bound (<25 MB delta post-GC), asymptotic multi-burst leak check (<15 MB gradient across 3×500-doc bursts), LRU eviction cap enforcement (256-item bound, 8 KB payloads, <10 MB post-GC); (2) tests/integration/databases/pool-contention.test.ts (2 tests): 20 concurrent background-worker-pattern tasks vs 20 concurrent foreground CRUD writes with zero-deadlock and sub-50 ms post-contention query latency; (3) scripts/verify-benchmark-local.ts generateLatencyVarianceReport(): grades the last recorded competitive-workload-replica run (rows selected by testFile, grouped by runId, newest run only) on its recorded p50/p99 percentiles and sample count, classifies as EXCELLENT/ACCEPTABLE/WATCH/HIGH_VARIANCE, and reports UNMEASURED — never an estimate — when percentiles are absent or fewer than 100 samples back them; aggregate rows (p50 = 0 ms) are listed as excluded so --strict stays passable (achievements §3.34 replaced the previous p50/p99 estimator and the hardcoded filename list). Also: background-runtime-env.ts replaces $app/env virtual module in the standalone Node worker (stops crash-restart loop in production); vite esbuild plugin compiles .svelte.ts rune modules inside the worker bundle; Prefer: return=minimal RFC 7240 fast path wired for both create and update; PostgreSQL addedColumns guard prevents redundant CREATE INDEX during schema migration. |
| Tests |
Unit suite: 518 files / 4512 tests green (3 new in heap-stability.test.ts, updated background-supervisor.test.ts). Integration: pool-contention.test.ts added to tests/integration/databases/. All pre-commit gates pass (6/6 in 2m2s). |
| Perf |
Prefer: return=minimal skips full document read-back on create+update (~0.5–1.5 ms saved per write on PG). Background worker no longer crashes on $app/env resolution (stops the crash-restart loop that suppressed sustained write RPS after 10k ops). PG addedColumns guard skips re-indexing already-indexed columns. |
| Docs |
achievements-2026 · roadmap-2026 |
| Open |
PostgreSQL matrix run for pool-contention.test.ts (SQLite locally, needs Docker for PG); P95/P99 variance report integration into CI summary output (currently a manual bun run scripts/verify-benchmark-local.ts --report=variance call; the flag family now matches and --strict-variance fails on a 🔴 or an unmeasured row). |
Smart Table platform + test acceleration & bottleneck elimination wave (2026-09-19)
|
|
| Status |
Shipped — Smart Table column resize and bulk field-edit platform complete: ColumnResizeHandle integrated across media, tokens, and users tables; non-bleed solid background on sticky pinned columns (SMART_TABLE_PIN_START/END); bulk-field-edit-modal.svelte with Svelte 5 runes, strict FieldInstance schema typing (zero any), ARIA compliance, and automatic field-type handling (text, number, boolean, date); wired via Alt+E shortcut in entry-list-multi-button.svelte and entry-actions.ts. |
| Tests |
Unit 515 files / 4509 tests green in 18.79 s; bun run check 0 errors / 0 warnings; risk:audit 5/5; lint:tenant -- --full clean. New test suites: tests/unit/components/create-smart-table.test.ts (column resize + dynamic setLayoutKey namespace persistence), tests/unit/utils/entry-actions.test.ts (bulkEditEntries, setEntriesStatus, archive/permanent delete), tests/unit/utils/api.test.ts (batch update endpoint). |
| Perf |
Test suite accelerated from 48.27 s → 18.79 s (~61% speedup, 2.57× faster): (1) Vitest worker pool dynamically scales with CPU cores (cpuCount >= 16 ? Math.min(16, cpuCount - 4) : cpuCount - 1) with VITEST_MAX_WORKERS override; (2) invalidateTurboAuthForUser moved to handle-turbo-get.ts decoupling hooks.server.ts dynamic import from user/auth mutations (auth-lockout.test.ts dropped 2,270 ms → 81 ms; user-attribute-update.test.ts dropped 1,439 ms → 75 ms); (3) testing-api-gate.test.ts static import replaced dynamic resetModules (466 ms → 75 ms); (4) seed-burst.ts retryDelayMultiplier: 0 removed 200 ms of real idle sleeps; (5) Docker Redis cmd.flushDb() test isolation. |
Authz guardrails + token/APQ/body-limit + 4-DB contract wave (2026-09-18)
|
|
| Status |
Shipped — two authorization bypasses closed: POST /api/user (bare namespace root) now requires user:write and GET /api/user requires user:read instead of inheriting the auth path exemption, with non-admin create payloads stripped of role/roles/isAdmin/roleIds/permissions; /api/token/* flipped from a public-route deny-list to a positive allowlist (only GET /api/token/validate-token/{value} is public — everything else needs admin or the mapped api:token permission, and PUT /api/token/{id} is field-allowlisted to expires/blocked/consumed, admins additionally email/role). GraphQL APQ binds the hash to the query (sha256(query) compared with timingSafeEqual, 64-hex shape enforced before any cache read/write). MAX_PAGE_SIZE = 200 is enforced centrally (REST parser, SDK find funnel, GraphQL fast path + resolver). The dispatcher’s 15 MB API_MAX_BODY_SIZE_BYTES ceiling now also counts chunked bodies through a zero-copy stream (413 PAYLOAD_TOO_LARGE); the media stream route receives that counted stream undrained and aborts multipart parts incrementally at the same cap. |
| Tests |
Unit 515 files / 4499 tests; SQLite integration 660 tests / 71 files (641 pass, 19 skip); the 7-file contract bundle 58/58 on SQLite + PostgreSQL + MariaDB + MongoDB; tsc --noEmit 0 errors; risk:audit 5/5. New suites: 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 bypass fails a test), 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 tests/integration/databases/{find-page-count,cache,content-nodes,auth-session-amr}-contract.test.ts and tests/integration/api/token.test.ts. |
| Bugs |
Two pre-existing product bugs found and fixed by this work (not known before): (1) every SQL sort emitter mis-mapped numeric directions — {status: 1} emitted status DESC while the keyset cursor said ASC, so keyset pagination repeated rows and terminated early on SQLite/MariaDB/PostgreSQL; one normalizeSortDirection() now feeds page-utils, drizzle-sql-helpers, sql-adapter-core and the query builder. (2) MongoDB reorderStructure never worked — it called mongoose.startSession() on the global default connection while the adapter uses mongoose.createConnection, so every drag-reorder returned success: false; it now uses a single ordered bulkWrite matching the adapter’s declared supportsTransactions: false. A third repair gap: MariaDB ADD COLUMN IF NOT EXISTS could never fix an existing mfaVerifiedAt datetime (fsp 0), so millisecond MFA timestamps silently truncated — the column is declared DATETIME(3) in the schema spec, so fresh installs are precise by construction (the one-off boot repair that widened pre-existing databases was deleted in September 2026 with the other legacy tails). |
| Perf |
zstd compression is async on the event loop (size-gated sync fallback at SYNC_MAX_SIZE 64 KiB); count-cache entries are tagged with both logical and physical collection spellings and the write path clears both (stale list totals for the 30 s TTL); content.nodes.bulkUpdate collapses N per-row DELETEs into one IN (...) and reorderStructure batches N per-item SELECT * into one projected IN (...) read; the security gate no longer computes and discards duplicate surface/UA scans; 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) ship in all three SQL dialects + the system schema spec with idempotent boot tails for existing installs; commerce order downloads do one batched product lookup instead of one per line. |
| Posture |
The wave’s two bypasses had one root cause — the set of paths that skip authorization was hand-maintained and nothing asserted it against the permission matrix. The guardrail suite is the durable fix: it fails a unit test when a new namespace root or public-route exemption appears without a reasoned contract entry. Remaining coverage asks stay in §5 (multi-tenant browser E2E, live IdP, worker-pool schema load). |
Build-parity guardrails + tenant client/server split (2026-09-18)
|
|
| Status |
Shipped — one mutation limiter (handle-rate-limit → rateLimit() only), branded systemScope replaces the forgeable bypassTenantCheck boolean, Mongo REST point-read via native collection.findOne, same-host socket/loopback wiring, Yjs entry ACL. Post-validation fixes: duplicate systemScope key in crud-methods, @utils/tenant.ts made client-safe (isMultiTenantEnabled/withTenant moved to tenant-isolation.server.ts), Yjs ACL helpers extracted to yjs-access.ts (+server.ts may not export them), isValidTenantId in tenant.server.ts to keep the esbuild config graph alias-free. |
| Tests |
Unit 501 files / 4294 tests, SQLite integration 651 tests / 70 files, full 4-adapter build, risk:audit 5/5 (full secret scan: 1422 files clean). New guardrails: tests/unit/config/vite-config-graph.test.ts, tests/unit/routes/endpoint-exports.test.ts, tests/unit/utils/tenant-module-boundary.test.ts — each ships a self-check; the graph guard is mutation-tested (fails when an alias import is injected into the config graph). |
| Posture |
More tests were not the gap — build-time parity was: the suite was green while bun run build failed and the secret scanner flagged a private-key read in a client-reachable module. The guardrails now fail those classes in ~0.4 s of unit runtime. Remaining coverage asks stay in §5 (multi-tenant browser E2E, live IdP, worker-pool schema load). |
| Hygiene |
tsc --noEmit went from 22 pre-existing errors to 0 (unsound WithId<Document> → T casts, string→DatabaseId branding, $app/environment → $app/env, a phantom CacheCategory.ENTRY, a mock missing getTokenByValue, benchmark typing). Type-checking is not part of any gate yet, which is why those had drifted in silently. vitest.config.ts is now in the project tsconfig.json include list, so config-level type errors can’t hide from tsc again. |
| Data |
Session amr + mfaVerifiedAt now persist on all four engines (spec + Drizzle + boot ALTERs + Mongo SessionSchema); rotation carries the MFA proof, and Mongo rotation works on standalone deployments instead of throwing. Covered by auth-session-amr-contract.test.ts; cost measured by auth-session-amr.test.ts (Δ below noise: −14.6 % SQLite / −9.5 % PostgreSQL). Benchmark tooling: the documented --only=<name> filter is implemented, and benchmark files missing from GROUPS now run instead of being silently skipped. |
Compilation transformer v6 + schema-loader lean pass (2026-09-04)
|
|
| Status |
Shipped — fix(compilation): rewrite widgets under schemas (0d860c2f6): the composite transformer returned after schema _id/tenantId injection without visiting children, so widget calls under exported schema.fields kept bare widgets identifiers and never received deterministic uuids. v6 descends after injection and injects uuids on the first-argument object literal (bare + pre-written globalThis.widgets.*, nested group/repeater shapes included). TRANSFORMER_VERSION bumped 5→6 (fingerprint forces clean recompile). |
| Tests |
New checked-in fixture tests/unit/compilation/fixtures/collections/{posts,helpers}.ts + tests/unit/compilation/widget-collection-regression.test.ts (4 tests) drive the real compile(): rewrite assertions, 3/3 deterministic uuids incl. nested, byte-identical fresh compiles, loader-parity import() with globalThis.widgets. Composite unit coverage extended in tests/unit/utils/transformers.test.ts (6 new cases). tests/unit/content/loader-utils.test.ts (10 tests): path confinement, hash determinism/sensitivity, native load of a real compiled schema, single-install widgets proxy. |
| Perf |
Schema loader lean pass (perf(content)): widgets proxy installed once instead of per-file await + global write; confinement roots + case-insensitive widget map precomputed; per-task dummy no-op timeout removed from the worker pool; generateSchemaHash single-pass without per-field objects/JSON.stringify. Boot-path probe: 150 compiled schemas load in 42 ms (0.28 ms each), hash ≈2 µs/call. Widget definitions now declare version explicitly (28 files) — registry import no longer warns at boot. |
| Bench |
raw-db-ceiling.ts measures the bare-HTTP floor before printing the ceiling table and reports CMS rows as % of raw HTTP floor (honest HTTP yardstick; driver-ceil % remains the fallback for SDK/DB-layer records). 2026-09-04 SQLite baselines recorded via BENCHMARK_RECORD=1: api-latency cold 3049 RPS / 2.396 ms, TURBO-HIT 5478 RPS / 1.368 ms @8c; middleware flamegraph per-stage costs. |
| Docs |
compilation-pipeline · benchmark_sqlite · test-to-docs-map · Achievements Log |
| Open |
Worker-pool schema loader (production path) still lacks an integration test that drives a widget-call collection through loadSchemaPooled — unit coverage exercises the native path only. |
Honest 4-DB benchmark matrix + hardware-adaptive tuning (2026-08-23)
|
|
| Status |
Shipped — full 4-DB matrix 200/200 on the hardware-profile build (run 3: SQLite 50/0 · MariaDB 50/0 · Postgres 50/0 · Mongo 50/0). Run 2 (pre-profile) was 199/200 — the lone dev-dependency-load failure was the DX toolchain flagging unformatted auto-generated benchmark MDX; the reporter now runs oxfmt on every written report, so bun run check stays green after runs. Benchmark-cheating bypass removed in Aug 12/14 commits kept all numbers honest (production-mode servers, real auth/rate-limit/WAF). |
| Product |
Hardware-adaptive profile (@utils/hardware-profile): ONE detection at process start published to the shared global registry — every module/chunk/worker reads the same object. Workload-prioritized CPU allocation (media > compile > workers > jobs > DB fan-out) under a global HARDWARE_CPU_BUDGET (default 0.75 all-in-one VPS, 1 for dedicated app servers) + per-knob env overrides (SHARP_CONCURRENCY, DB_POOL_SIZE, MODULE_WORKER_POOL_SIZE, COMPILE_CONCURRENCY, JOB_CONCURRENCY, MONGO_MIN_POOL_SIZE, UV_THREADPOOL_SIZE). Wired into hooks (sharp/threadpool), all 4 DB adapters (pools), module-loader workers, compile, job queue, compression (weak boxes cap gzip/brotli), setup wizard + dashboard (/api/dashboard/system-info exposes the profile). Media: preset (width,format) dedup (20→12 jobs) + deferred responsive-variant generation → SDK 210→116 ms (−45%), HTTP 208→105 ms (−50%), still emitting all variant records. CacheService: flat-key pattern invalidation now buckets under the namespace key → 26→4.5 ms. |
| Bench |
Matrix run 3 (2026-08-23, profile build): SQLite 50/0 · MariaDB 50/0 · Postgres 50/0 · Mongo 50/0 (200/200). Run 2 vs run 3 (pre-profile vs profile): no systematic regression — the feared GQL-100c pool regression from smaller DB pools did NOT materialize (GQL 100c flat-to-better: SQLite −4%, PG ±0%, MariaDB avg flat); media SDK −3%; notable Postgres gains (widget pipeline −44%, Yjs −37%, mixed workload −25%, SEO −17–27%) and Mongo gains (collection search −29%, uploads −17–20%). Remaining “degraded” flags are sub-5 ms run-to-run noise (cache pattern-invalidation, Yjs CRDT, small payloads). Restart overhead cut ~20%: post-destructive restarts are now data-driven — only graphql-stress (→ relational-performance) and large-payload-streaming (→ migration-scale) restart unconditionally (verified breakers); the other 5 destructive tests skip the ~12–16s boot, saving ~4 min of the ~22 min run. Cross-DB ledger vs Aug baseline: Full Journey @1c −89%/−65%/−67%/−82%, State Transition −64%/−36%/−72%, Bulk Migration 10k −46%/−64%, Cache Miss Cold −80%, Argon2id −48% (SQLite). MongoDB stress flakes fixed via pre-stress full readiness wait + bounded crash-retry in the matrix harness. New provisioning: bench_index_pressure + bench_migration_large collections seeded (kills the 100k-row SQLITE_ERROR no such table). |
| Unit |
Full suite 416 files / 3667 tests green; bun run check clean. Added statistics-accuracy.test.ts (raw-sample tail percentiles, t-distribution CI, IQR-trimmed mean) + cache pattern-invalidation case. |
| Docs |
benchmark-matrix · benchmark_sqlite · performance-architecture · Achievements Log · roadmap |
| Open |
Redis matrix variant (USE_REDIS=true, _redis reports) still to run. GPU: sharp/libvips has no GPU backend — media parallelism is CPU-tier capped by design. |
Cold-start write-path engine + WCAG-AA palette + media-tree click fix (2026-08-22)
|
|
| Status |
Shipped — cold-start/write-path optimizations verified green across gates; E2E contrast + media move regressions fixed (both were real product bugs, not test-only) |
| Product |
Widget registry registers pre-scanned widgets synchronously in the constructor; prewarmCollectionSchemas() pre-fills the SDK schema cache + adapter registries at READY; _provisionedTables fast-path skips repeated DDL on all 4 adapters (Postgres does a single information_schema.columns read + ALTER/backfill only missing columns — kills the 25k-row cold-start UPDATE); self-healing getModel reconstructs the wrapped model from the cached table def without request-thread DDL; lazy write-path modules pre-warmed at READY; cache-warming L1 re-warm every 4 min (before collection: TTL) |
| E2E |
mediagallery/move-remote.spec.ts — nested treeitem clicks bubbled to the root node whose onClick re-navigated to /mediagallery, cancelling the folder goto(); fixed with e.stopPropagation() in tree-view.svelte (5/5 pass). login/accessibility.spec.ts — primary/tertiary/success/warning-500 + preset-filled-surface-500 darkened to WCAG AA (was 1.94–3.74:1), login floating-label surface-500→surface-700, axe audit waits for the sign-in transition:fade to finish (opacity <1 blends colors → phantom 1:1 violations) (3/3 pass) |
| Unit |
Full suite 399 files / 3576 tests green; bun run check (format + lint + dead-classes + design-token checker + lockfile-sync) clean |
| Integration |
SQLite adapter-level tests/integration/databases/ 161/161 pass; server-bound API suites require a TEST_MODE=true harness build (plain bun run build deploy-strips /api/testing → API_ENDPOINT_NOT_AVAILABLE on every seed/reset) — use scripts/runs/run-integration.ts or build with TEST_MODE=true |
| Docs |
performance-architecture · style-guide-gui · accessibility-audit |
| Open |
Full E2E chromium sweep + 4-DB integration matrix still CI-only; verify no visual-regression drift from the darkened palette (env-gated) |
Multi-DB batching/caching engine + turbo-cache tenant isolation (2026-08-21)
|
|
| Status |
Shipped — zero-allocation tenant clauses, memoized table-name/route classification, 4-DB parity on multi-row batch inserts; fixed mid-glob L1 cache invalidation and test-tenant header honoring in turbo GET + turbo-auth fast paths; 4 previously-red integration files now green (webhooks, automations, token, tenant-isolation) |
| Product |
EMPTY_TENANT_CLAUSE + ASCII-first JSON sniff in relational-utils; bounded memoization in collection-name / drizzle-sql-helpers / route-resource-state-machine; O(1) pointer-based BFS in content-utils; rawInsertManyReturning multi-row batches on MariaDB/PG/SQLite; clearLocalL1ByPattern glob support (res:*webhooks* now purges); handleTurboGet + handleAuthentication fast paths prefer x-test-tenant-id over cached session tenant (no cross-tenant turbo leaks) |
| Unit |
tests/unit/core/count-cache.test.ts (arg-count contract assertions) · tests/unit/plugins/commerce.test.ts (cart row hardening: NaN/currency-mismatch 500s) · GraphQL resolver context types now declare publicationFilter (4 files) · collections-namespace cast hygiene |
| Integration |
tests/integration/api/webhooks.test.ts · api/automations.test.ts · api/token.test.ts · databases/tenant-isolation.test.ts — 39/39 pass (previously 4 failing across all DB adapters) |
| Docs |
cache-system · performance-architecture · multi-tenancy |
| Open |
Full multi-DB integration matrix + E2E shards still CI-only (SQLite local green); Redis L2 multi-node still not part of the local gate |
Hooks hardening — field write guards, session dedup, real-time (2026-08-10)
|
|
| Status |
Shipped — field-level write guards wired into the mutation path; WS session resolution deduped onto the HTTP pipeline; 18-hook READY pipeline; real-time stack is SSE + Yjs; full unit suite green (366 files / 3302 tests) |
| Product |
assertWriteAllowed / hasGuardedFields / getCollectionFields (30s schema memo) in handle-api-requests — 403 + UNAUTHORIZED_ACCESS audit for guarded-field writes; resolveSessionForWebSocket shares the HTTP session pipeline (LRU→store→Redis→DB, negative cache, single-flight); yjs:sync SSE bridge makes multi-editor updates round-trip; /ws upgrades session-validated (fail-closed) |
| Unit |
tests/unit/services/field-write-guard.test.ts (15) · field-permission-service.test.ts (19) · tests/unit/live/client-import-boundary.test.ts (2) · hooks/auth/security suites (615) |
| Integration |
tests/integration/api/rtc.test.ts (SSE /api/events happy + 401) |
| Bench |
tests/benchmarks/hooks-performance.test.ts — full auth pipeline 0.612 ± 0.010 ms (3-run mean, ~1,363 RPS) vs 0.856 ms July baseline (~29% faster); post-tweak re-run within noise |
| Docs |
field-level-access-control · collaborative-editing · real-time updates · server-hooks |
| Open |
GraphQL mutations bypass the field write-guard (documented boundary) |
Request Lane Router + response cache (2026-08-06)
|
|
| Status |
Shipped — O(1) classifyRequest in hooks.server.ts; x-svelty-lane on responses; turbo GET + GraphQL/REST user-scoped responseCache; fixed session cookie name (auth_sessions, not svelty_session) for HYPER_TURBO |
| Product |
Lanes: FAST_STATIC, HEALTH, HYPER_TURBO, API_READ/WRITE, APP_SSR, BOOTSTRAP, FILES, PUBLIC_SITE; health/static fast-paths; handleTurboGet TURBO-HIT when warm turbo-auth + L1 body |
| Unit |
tests/unit/core/request-classifier-and-response-cache.test.ts · tests/unit/hooks/handle-turbo-get-lane.test.ts |
| Integration |
tests/integration/api/request-lane-headers.test.ts (x-svelty-lane, health JSON, dashboard __data.json) · session-page-load still P0 for auth→SSR |
| E2E |
tests/e2e/routes/system/hooks-lane-smoke.spec.ts — admin session shell + /health |
| Bench |
tests/benchmarks/lane-router-attribution.test.ts |
| Docs |
performance-architecture (operator guide + lane table) |
| Open |
Optional: assert TURBO-HIT under live integration with seeded turbo-auth warm path; multi-node Redis L2 load tests |
List / count product layer (2026-08-04)
|
|
| Status |
Shipped adapter + service wiring — not “full UI polish”; E2E only after more list surfaces adopt findPage |
| Product |
findPage (hasMore + keyset), CountMode, L1 count cache; CollectionService limit+1 + cached count; media getByFolder; users listUsers findPage; facets via crud.count |
| Unit |
tests/unit/core/page-utils.test.ts, count-cache.test.ts |
| Integration |
tests/integration/databases/find-page-count-contract.test.ts (SQLite local; multi-DB in CI matrix) |
| Bench |
tests/benchmarks/database-performance.test.ts (FIND PAGE / LIST+COUNT / COUNT ESTIMATE / CACHED / keyset) |
| Docs |
performance-architecture · database-tests · roadmap |
| Open |
Saved views on users/media; OIDC PKCE + login UI |
Marketplace + OIDC (2026-08-04)
|
|
| Status |
Partial product — in-app catalog tab + OIDC login/callback + JWKS verify |
| Product |
Extensions Marketplace tab; GET /api/marketplace; oidc-login / oidc-callback |
| Unit/E2E |
No dedicated E2E yet — add seed-backed golden when install + IdP UI ship |
| Docs |
roadmap-2026 |
Session & RBAC hardening (2026-08-04)
|
|
| Status |
Shipped — three hardening rounds; full unit suite green (3073) |
| Product |
Device policy (single-per-device / single-per-user / allow-multiple), SESSION_TTL_HOURS, SESSION_IDLE_HOURS, block propagation purge, credential-free session snapshots, log-only IP/UA anomaly detection, SESSION_MAX_PER_USER LRU cap, step-up re-auth for cross-session revoke, admin session console API |
| Unit |
tests/unit/auth/auth-lockout.test.ts (device policy, TTL, max sessions) · tests/unit/auth/sessions/session-user.test.ts (snapshot stripping, store hygiene, anomaly evaluation) · tests/unit/hooks/authentication.test.ts (stripping, warm-cache, anomaly) · tests/unit/api/user.test.ts (reauth proof, admin console) |
| Integration |
Covered by existing auth integration suites (session create/revoke/rotate) |
| Bench |
tests/benchmarks/hooks-performance.test.ts — full auth pipeline 0.612 ± 0.010 ms (3-run mean, ~1,363 RPS) vs 0.856 ms July baseline (~29% faster) |
| Docs |
login-security · cache-system · hook-test-coverage · roadmap |
| Open |
Admin session console UI (Access Management); browser golden for revoke flow; anomaly → alerting integration |
ContentSync / compilation / Collection Builder (2026-07-31)
|
|
| Status |
Unified coordinator — IDE edit + GUI save + boot share one path; soft HMR; surgical client patch when possible |
| Product |
syncContentState reasons: boot · watcher · collection-save · gui-save · sidebar-reorder; GUI compile lock; atomic .js; hash-backed drift; tenant paths via tenant.server |
| Unit |
sync-content-state (13+), collection-save-sync (2), schema-contract (6+ Valibot), compile-manifest (atomic/noOp/changedJsPaths), collection-store-reactive (patchActiveSchema) |
| E2E |
collection-builder/builder.spec.ts — shell + soft-refresh + structure (category create survives reload) + golden schema→entry→API |
| Docs |
compilation-pipeline · content-system · collection-builder |
/user — profile & management (2026-07-19)
|
|
| Status |
100% control map — product + unit + integration + E2E |
| E2E |
7 specs: smoke, profile, management, invite, complete-coverage, p0-journeys, coverage-100 |
| Product |
Profile update: command remote + email fallback when session user_id stale after re-seed; plain-text toasts for assertions |
| Docs |
routes/user.mdx · e2e-coverage-matrix §3 |
/dashboard — shell + licensing (2026-08-04)
|
|
| Status |
Shell 100% (widget catalog is install-specific — never fixed list) + server-side license gates |
| E2E |
dashboard.spec.ts — empty/grid, add/search/reset, pointer + keyboard reorder, toolbar, plugin slot |
| Unit |
page.server permissions, widget-defaults categories, dashboard-license-gate (map + 403 paths) |
| Integration |
dashboard.test.ts health/metrics/logs/… APIs widgets consume (premium endpoints pass during trial) |
| Docs |
routes/dashboard.mdx |
/mediagallery (2026-07-18)
|
|
| Status |
Shell + upload/delete + editor save + folders + bulk + move + remote upload |
| E2E |
mediagallery, image-editor, folders-bulk, move-remote |
| Unit |
mediagallery-page-server.test.ts |
| Docs |
routes/mediagallery.mdx |
/config/access-management (2026-07-19)
|
|
| Status |
Tabs + create role + save/reset + admin/tokens; sticky action .first(); tokens panel testids; permission-matrix toggle |
| E2E |
config/access-management.spec.ts — create-role outcome, tokens panel (website-tokens-panel); system/permissions.spec.ts — matrix toggle + persistence; system/language.spec.ts — language selector |
| Product |
Role modal response as 3rd trigger arg; website-tokens $effect loop fixed (density/columns sync + untracked fetch); fetchPageData must select the page __data.json node (not the layout’s) |
| Unit |
access-management-page-server.test.ts admin gate |
| Docs |
routes/access-management.mdx |
/config/system-settings (2026-07-18)
|
|
| Status |
Group nav/search, edit→save/discard, save+reload happy path, export/import, MT/GDPR |
| E2E |
system/settings.spec.ts (shell + edit/discard + persistence) |
| Unit |
page.server gate + settings-groups + settings-utils |
| Integration |
settings.test.ts export/import |
| Docs |
routes/system-settings.mdx |
/config/redirects (2026-07-19)
|
|
| Status |
Admin gate + golden create→search→delete; primary store = redirectsMV (aligned with middleware) |
| E2E |
config/redirects.spec.ts golden (MV list/search/delete) |
| Unit |
redirects.server (MV insert/update/delete + normalizeRedirectRow) + redirects-utils + page load |
| Product |
source/target on MV; content collection mirror best-effort; compound index idx_redirects_mv_lookup (tenantId, source, active) |
| Docs |
routes/redirects.mdx |
/config/queue + /config/monitor (2026-07-18)
|
|
| Status |
Admin gates, testids, queue filter URL + showConfirm; monitor resilient load |
| E2E |
config/operations.spec.ts |
| Unit |
queue-page-server + monitor-page-server |
| Docs |
queue, monitor |
/config/webhooks — Testing 2026 reference (2026-07-18)
|
|
| Status |
ADR reference — full three layers: unit + HTTP integration + golden E2E |
| E2E |
config/webhooks.spec.ts (shell guard + golden journey only) |
| Unit |
page.server + webhooks-utils + webhooks-api + fetchApi CSRF |
| Integration |
tests/integration/api/webhooks.test.ts — list/create/delete + 401 unauth + editor deny |
| ADR |
adr-testing-2026.mdx |
| Docs |
routes/webhooks.mdx |
/config/extensions (2026-07-18)
|
|
| Status |
Admin via isAdmin, tabs + plugins testids, toggle CSRF/toast |
| E2E |
config/extensions.spec.ts |
| Unit |
extensions-page-server.test.ts |
| Docs |
routes/extensions.mdx |
/config/automations (2026-07-19)
|
|
| Status |
automations-api + fetchApi CSRF; golden builder create→list→edit→delete; seed-search |
| E2E |
config/automations.spec.ts — open editor via card link (not bare text / checkbox label) |
| Unit |
page.server + automations-api |
| Docs |
routes/automations.mdx · ADR |
/config/workflows (2026-07-19)
|
|
| Status |
Shell + seed-workflow golden; name NOT NULL safe; states JSON normalized on read |
| E2E |
config/workflows.spec.ts (toolbar/canvas + seed→GET by collectionId) |
| Product |
workflow-service.saveWorkflow default name; testing API passes name; builder name fields |
| Docs |
routes/workflows.mdx |
/config/sync + /config/trash (2026-07-18)
|
|
| Status |
Admin gates, sync tabs/status testids, trash empty/table + restore confirm |
| E2E |
config/data-ops.spec.ts |
| Unit |
sync-trash-page-server.test.ts |
| Docs |
routes/sync-trash.mdx |
Ultra-smart auth & client safety (2026-07-18)
|
|
| Status |
P0 hardened — loopback cookies, testing dual-write, exhaustive import-boundary, session→18 admin pages |
| Unit |
session-cookies, client-import-boundary (full client-root walk), login-cookie contract, page-guards, builder page.server, totp-encryption, registry-auth-hooks |
| Integration |
session-page-load · webhooks · automations · config-admin-surface · collection-structure · security-negative config matrix |
| Docs |
headless-test-inventory.mdx · auth.mdx |
2FA Hardening & Plugin Auth Hooks (2026-07-30)
|
|
| Status |
35 new unit tests — AES-256-GCM TOTP encryption (round-trip, tamper detection, legacy compat), trusted device tokens (gen/verify/reject), afterAuthenticate hook (deny/requires2FA/fail-open/skip disabled), configurable TOTP window |
| Unit |
tests/unit/auth/totp/totp-encryption.test.ts (20 tests), tests/unit/plugins/registry-auth-hooks.test.ts (15 tests) |
| Product |
TOTP secrets encrypted at rest; pending setup state (resumable enrollment); __Host-2fa-trusted-device cookie; plugin auth hooks in signInInternal |
| Docs |
auth.mdx · authentication-system.mdx · plugin architecture |
Databases whitebox + enterprise storage (2026-07-20)
|
|
| Status |
tests/unit/databases/ green (0 skip for soft-delete under Bun; magic-link + CacheService fixed). Real proofs — not mock-only theater for Auth lockout, credential cache, boot helpers |
| Unit |
cache-service (always-tenant keys, pattern clear), credential-auth-cache (real L1), auth-lockout (real Auth), soft-delete (mongoose stub), database-resilience / CORRUPT_CONFIG, magic-link privacy |
| Product |
CacheService set/get tenant default alignment; clearByPattern wildcard tenant; fail-fast CORRUPT_CONFIG → MISSING_CONFIG |
| Enterprise |
Transactional outbox, plugin storage, schema lifecycle hooks (beforeValidate/afterValidate), media jsonPath — unit + integration seeds |
| Manifest |
tests/critical-test-paths.ts (replaces p0-manifest/registry) · runners: scripts/runs/run-integration.ts, scripts/runs/run-e2e.ts, scripts/test/test-doctor.ts |
| Docs |
database-tests.mdx · plugin-storage · outbox · schema hooks |
Other solid areas (summary)
| Area |
Notes |
| Auth & users API |
Strong integration (login, batch, avatar, sessions, 2FA) + page-load session |
| Security hooks |
Dedicated regression suite in pre-commit/push |
| Collection builder |
E2E = shell + soft-refresh + golden (builder.spec.ts); structure/utils unit+integ; ContentSync unit suite |
| Content / compile |
ContentSync coordinator + schema contract + compile manifest; see hot area above |
| Site starter / preview |
Smoke E2E + unit; deep iframe edit still open |
| Data operations |
Strong unit; thin browser E2E |
| Multi-tenant |
Unit/guard coverage; E2E isolation spec exists, gated behind MULTI_TENANT=true |
| Stores unit |
tests/unit/stores/ (reactivity, patchActiveSchema, structure fingerprint, floating-nav-store 22) |
| Admin chrome |
command-palette (18) + floating-nav-store (22) + logger (7) + widget-naming (12) unit tests; hotkeys Mod+K/Alt+G unified |
Deeper inventories: headless-test-inventory · e2e-coverage-matrix · three-layer-completeness.
5. Open gaps (actionable only)
Only open product/test risks. Closed items stay in route docs / ADR changelog — not duplicated here.
| Gap |
Priority |
Why it matters |
| Multi-DB contract without asterisks |
✅ Closed |
Cross-engine contract bundle is green in CI; bun run test:parity now mirrors it locally on every reachable engine (verified all four on 2026-10-18) |
| Lane TURBO-HIT live warm path |
🟢 Lower |
Unit covers TURBO-HIT plus the 2026-09-27 gates (endpoint parity, FLAC exemption, grant epoch); a full HTTP warm turbo-auth seed in integration stays optional |
Wire fail-closed branch (no status column) |
🟢 Lower |
requirePublished fails closed where a table has no status column and the base-class fallback re-checks the row, but every provisioned table carries status, so the branch is reasoned in code and unit-guarded only — add an integration case with a status-less table |
| Two reproducible E2E specs (local host) |
✅ Closed |
Both were product bugs, not host state, and both are fixed: /config/design-system’s overrides $effect read the state it wrote, so every local edit was reverted to the persisted value before saving (writes now scoped with untrack), and the avatar writers (LocalCMS.auth.saveAvatar/deleteAvatar) bypassed the only invalidator of getFreshLayoutUser()’s 15 s layout snapshot, so the modal saw avatar: null and never rendered the delete button (both writers now invalidate). Verified: profile.spec.ts green across --repeat-each=3, design-system.spec.ts 18/18 across three repeats |
| Type check is in the gate |
✅ Closed |
bun run check runs tsc --noEmit + svelte-check; the 2026-10-18 pass fixed the 31 tsc and 42 svelte-check errors (core adapter generics, $props<T>(), widget types) and CI runs the same typecheck group |
| Security-core 0 % files |
✅ Closed |
Real suites now cover two-factor-auth (0.63 → 86.71 %), handle-security (0 → 87.22 %), submission-guard (0 → 100 %), google-auth (0 → 88.61 %) and SSO/OIDC (33.33 → 93.17 %, including discovery/JWKS/exchange with mocked egress and real node:crypto). |
| Admin route forms |
✅ Closed |
jsdom suites cover System Settings (11), Access Management role modal (8), and Workflow editor (6). Forms outside these three still run in CI E2E shards. |
| E2E force-click workarounds |
✅ Closed |
User-management and permission checkbox/confirm clicks no longer force; the visible-label helper replaces evaluate(el.click()) for sr-only Checkbox inputs. Remaining force usages are limited to animated/consent overlays with documented reasons. |
| Explicit flake budget |
✅ Closed |
scripts/check/check-flake-budget.ts gates retried-pass counts per shard; a single retry is governed by --max-flaky, while the percentage rate only applies when more than one test retried and at least 20 executed. CI runs it after every shard; pre-push runs it with --max-flaky=0. |
| Full E2E remains CI-only |
🟢 Lower |
The full 9-shard matrix remains CI-only. Pre-push runs the 7-test smoke plus the path-mapped behavioural/visual specs, so regressions outside those paths still wait for CI; a live IdP and route forms outside the local DOM suites are the largest remaining surfaces. |
| Benchmark: parallel-group contention |
✅ Closed |
Split Feature HTTP Read in scripts/benchmark-matrix/index.ts: light read benchmarks remain parallel while Heavy Query & Mixed Workload (relational-performance, mixed-workload) runs in its own sequential lane, eliminating SQLite lock contention without lowering any reliability guard |
| Multi-tenant browser isolation |
🟡 Medium |
E2E spec exists (isolation.spec.ts), gated behind MULTI_TENANT=true env |
| Session vs DB identity after wizard reset |
🟡 Medium |
Phase-1 wizard can race firstuser; profile has email fallback — harden setup order |
| Redirects/queue remotes (no REST) |
✅ Closed |
Remote admin + redirectsMV primary + unit; queue-actions |
| Config residual goldens (access/auto/wf) |
✅ Closed |
Local focused chromium 16/16 (2026-07-19) after product fixes |
| Data operations deep E2E |
🟢 Lower |
Sync/trash shells covered; migration wizard still plugin-gated |
| Worker-pool schema load (prod) |
🟢 Lower |
Widget-call collections covered by unit only (real compile() + native loadSchemaNative); loadSchemaPooled/worker chunk untested in integration |
| Cache L2 (Redis) integration |
✅ Closed |
L2 contract suite (cache-service-l2-contract.test.ts) — FakeRedis always-on + real Redis via TEST_REDIS_URL; fixed untagged delete propagation to remote L1 |
| GraphQL browser E2E |
❌ Skip |
Integration only — do not add browser GraphQL as a gate |
| SSO/OAuth real IdP |
🟡 Medium |
OIDC login/JWKS/exchange/logout paths are now covered with mocked egress and real node:crypto; CI still does not exercise a live third-party IdP, and the PKCE login UI is open. |
| List UI full findPage adoption |
🟡 Medium |
Service/adapters ready; users/media saved views + more goldens still thin |
| Marketplace install path |
🟢 Lower |
Browse/catalog in-app; one-click install still incomplete |
| Session revoke browser golden |
🟢 Lower |
Re-auth proof + admin console API unit-covered; E2E golden when Access Mgmt UI ships |
/ws collaboration auth |
✅ Closed |
/ws upgrades session-validated via ws-auth-registry → hooks.ws upgrade() (same pipeline as HTTP); fail-closed 401; session-authoritative tenant keying |
| Awareness over SSE |
✅ Closed |
Awareness POSTs to /api/collaboration/yjs (awareness: true) → yjsService applies + broadcasts yjs:awareness over SSE; client applies remote state with origin: "server" (no echo loop); yjs-awareness.test.ts covers the round-trip |
| Builder E2E sprawl |
✅ Closed |
Consolidated to shell + golden; 8 specs removed |
| Build-only failure classes |
✅ Closed |
Non-handler +server.ts exports, path-alias imports in the esbuild config graph, and private-key reads in client-reachable modules are now unit guardrails (endpoint-exports, vite-config-graph, tenant-module-boundary) instead of build/scanner surprises |
| Plugin deep E2E, breadcrumb media drag-move |
🟢 Low |
Optional polish |
6. What to run locally
# Pre-commit parity (format + type gate + risk + unit + real-DOM components)
bun run check
bun run risk:audit
bun run test:unit
bun run test:unit:components
# Before push (mirrors .githooks/pre-push)
bun run gate
# Local database parity (contract suites on every reachable engine)
bun run test:parity
bun run test:parity -- --only=sqlite,postgresql
bun run test:parity:docker # boot missing engines via compose, then run
# Coverage floors (P0 packages; thresholds in vitest.config.ts)
bun run test:coverage
# Browsable HTML report (opt-in; the v8 HTML reporter is unstable on Windows)
COVERAGE_HTML=1 bun run test:coverage
# E2E flake budget (reads the JSON written by the last Playwright run)
bun run flake:budget
# Measure flakes locally: one retry makes a recovered test visible as `flaky`
PLAYWRIGHT_RETRIES=1 bun run test:e2e -- --grep="(routes/user/)"
# Browser smoke only (reuses build/; auto-rebuilds if source is newer)
bun run test:e2e:smoke
# Unit only
bun run test:unit
# Inner loop — three scopes, pick the smallest that covers your change (Vitest only, never bun:test):
bun run test:unit:area tests/unit/utils/ # 1. fastest (~3.3 s): one dir/file, skips the changed-graph build; extra args forward to Vitest
bun run test:unit:changed # 2. selective: unpushed commits + staged + worktree/untracked vs a resolved base.
# Prints the base it chose (@{upstream} → origin/next → origin/main → next → main → HEAD~1,
# pin with --base=<ref>) + the affected file/test count; empty diff or no affected
# test ⇒ loud ⚠️ + exit 0, unresolvable base ⇒ exit 3. Never a silent green.
bun run test:unit # 3. the gate (pre-commit): always exhaustive, intentionally NOT selective — never wire a selector into it.
# Build-parity guardrails (static, <1s): config graph aliases, endpoint exports, tenant boundary
bun run test:unit -- tests/unit/config/vite-config-graph.test.ts tests/unit/routes/endpoint-exports.test.ts tests/unit/utils/tenant-module-boundary.test.ts
# NOTE: vitest's `isolate: false` hint is not usable here — cross-file mock/singleton
# leakage fails 37 files / 147 tests (measured 2026-09-18). `--pool=vmForks` is rejected
# too: 2.77 s vs 5.16 s on a 106-file slice, but the full suite failed 2–5 random files
# per run (setup-file `vi.mock` realm + timing) and swung 29.7 s ↔ 71.7 s. Stay on forks.
# Passing tests no longer print captured stdout (`silent: "passed-only"` locally); when
# debugging a passing test's logs, run the Vitest command directly with `--silent=false`.
# Lane router + turbo cache (2026-08-06)
bun run test:unit -- tests/unit/core/request-classifier-and-response-cache.test.ts tests/unit/hooks/handle-turbo-get-lane.test.ts
# After build: lane HTTP headers
bun run scripts/runs/run-integration.ts --no-build tests/integration/api/request-lane-headers.test.ts
# Turbo-cache tenant isolation + cache glob invalidation (2026-08-21)
bun run scripts/runs/run-integration.ts --no-build tests/integration/api/webhooks.test.ts tests/integration/api/automations.test.ts tests/integration/api/token.test.ts tests/integration/databases/tenant-isolation.test.ts
# Field write guards + field permissions (2026-08-10)
bun run test:unit -- tests/unit/services/field-write-guard.test.ts tests/unit/services/field-permission-service.test.ts
# Ultra-smart unit slice (auth, boundary, queue/redirects remotes)
bun run test:unit -- tests/unit/auth/sessions/session-cookies.test.ts tests/unit/live/client-import-boundary.test.ts tests/unit/api/testing-login-cookie-contract.test.ts tests/unit/utils/page-guards.test.ts tests/unit/routes/collectionbuilder-page-server.test.ts tests/unit/routes/queue-remote-admin.test.ts tests/unit/routes/queue-actions.server.test.ts tests/unit/routes/redirects-remote-admin.test.ts tests/unit/api/namespace-ownership.test.ts
# Databases whitebox + Auth lockout (real CacheService / Auth / boot)
bun run test:unit -- tests/unit/databases/ tests/unit/auth/auth-lockout.test.ts
# List/count product layer (pure + SQLite integration contract)
bun run test:unit -- tests/unit/core/page-utils.test.ts tests/unit/core/count-cache.test.ts
bun run scripts/runs/run-integration.ts --no-build tests/integration/databases/find-page-count-contract.test.ts
# 2FA encryption + trusted devices + plugin auth hooks (35 tests)
bun run test:unit -- tests/unit/auth/totp/totp-encryption.test.ts tests/unit/plugins/registry-auth-hooks.test.ts
# ContentSync / compile / schema contract / builder soft-HMR slice
bun run test:unit -- tests/unit/content/sync-content-state.test.ts tests/unit/content/collection-save-sync.test.ts tests/unit/content/schema-contract.test.ts tests/unit/compilation/compile-manifest.test.ts tests/unit/stores/collection-store-reactive.test.ts tests/unit/collectionbuilder/
# Integration (SQLite) — session-page-load, lane headers, webhooks, security-negative
bun run test:integration
# reuse build: bun run scripts/runs/run-integration.ts --no-build
# E2E CI-parity (preview :4173 — not Vite; one worker by default, like CI)
bun run test:e2e
# Override the worker count when you accept cross-file SQLite contention
bun run test:e2e -- --workers=2
# Fast browser smoke (7 tests; pre-push runs this against the harness build)
bun run test:e2e:smoke
# Hooks/lane smoke (needs auth-setup admin.json):
# bun run test:e2e:quick -- tests/e2e/routes/system/hooks-lane-smoke.spec.ts
# Collection Builder shell + soft-refresh + golden
bun run test:e2e --project=chromium tests/e2e/routes/collection-builder/builder.spec.ts
# Focused residual config/user goldens (example)
bun run test:e2e --project=chromium "--grep=redirects|Edit User Details|website tokens|automations|workflows|webhooks|Dashboard shell|create role"
# Smart test selector (auto-detects from git diff)
bun run test:smart
Safety: tests must never point at production DBs — see test-config safety guards in git-workflow.mdx.
7. Where to dig deeper
8. How to keep this page honest
When you ship a meaningful test or stability change:
- Update this file if health, open gaps, or hot areas change (few sentences + table rows).
- Put deep control maps and file-level inventories in route docs or headless-test-inventory — not a separate archive.
- Never treat static totals as CI truth — GitHub Actions is the live board.
- After changing a
.svelte control with a click/keyboard/modal path, run bun run test:unit:components and add an interaction test — SSR-only assertions cannot prove it is wired.