clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/fix-sync-enrollment-startup
lofi / docs / spikes / jazz2-baseline / evidence / 2026-07-15.md
2026-07-15.md 14.7K source

Jazz 2.0.0-alpha.53 baseline evidence

Date: 2026-07-15
Repository commit under test: 00c7f18
Host: macOS 26.5.1 (25F80), Apple silicon
Runtime: Deno 2.9.0, V8 14.9.207.2, TypeScript 6.0.3
Browser: Playwright Headless Chrome 150.0.0.0
Package: jazz-tools@2.0.0-alpha.53
Tarball SHA-1: d8e6f902498c13234da9cf02e530880bab183c4e
Tarball integrity: sha512-GSAt9axXPaZY3g0bZxEPeAmq/q4IWfJRdVMWmNYxLnaKzFHD3vYQf1y4VK9/ZAGCdabcxS8Im3dreISRHXkHSg==

The npm tarball and deno.lock agree on version and integrity. The package does not publish a gitHead. Jazz's main branch at 3384730e1c4509524b38c52fc9102fc6753411aa was inspected only for context and is not represented as the source commit for this package.

Repeatable commands

deno check spikes/jazz2-baseline/*.ts spikes/jazz2-baseline/src/*.ts
deno task spike:jazz2:build
deno task spike:jazz2:dev
deno task spike:jazz2:cloud
deno task check:secrets

Browser procedure (using playwright-cli; equivalent DevTools actions are acceptable):

  1. Start deno task spike:jazz2:dev --port 4177 --strictPort, open the printed URL, and snapshot.
  2. Add retained-alpha53-control; wait until the UI says confirmed local; reload and verify the row remains.
  3. Get the row ID from li[data-note-id] and call globalThis.__LOFI_JAZZ2_PROBE__.update(id, "after-update"); verify the subscribed list and durability state change, then reload again.
  4. Open a second tab at the same origin, add multitab-follower-write, and verify it appears in both tabs.
  5. Set the browser context offline, add offline-local-write, wait for local confirmation, restore the network, and verify both tabs converge without reloading.
  6. Start deno task spike:jazz2:cloud --port 4178 --strictPort, add cloud-global-alpha53, and wait until the UI says global write confirmed.
  7. Save browser storage state to ignored .playwright-cli/cloud-identity-state.json, load it in a fresh browser context, open the cloud URL, and verify the cloud row downloads. The state file contains the bearer identity and must never be committed or printed.

The cloud task publishes the spike schema to the configured Jazz application. It projected only the public app ID and server URL to the client. The test retained one note in that application; no credential value is recorded here.

Results

Control Result Evidence
Deno type-check pass Exact npm pin resolved through deno.json and deno.lock.
Deno production build pass Vite transformed 371 modules without a user-facing Node command.
Persistent-driver boot pass after compatibility fix Chrome reported OPFS, SharedWorker, MessageChannel, and Web Locks; Jazz opened its persistent driver.
Immediate local write pass Subscription rendered the row before network confirmation.
Query and update pass subscribeAll returned the row; Db.update changed it, reached local durability, and survived reload.
Local durability pass WriteResult.wait({ tier: "local" }) resolved.
Reload retention pass The row remained after a full page reload.
Offline write pass Browser network state was offline; a row rendered and reached local durability.
Multi-tab pass A second tab wrote through the follower path and both tabs received two rows.
Managed-cloud durability pass WriteResult.wait({ tier: "global" }) resolved against the configured Jazz server.
Clean second cloud client pass A second browser context with the same restored identity and no copied OPFS data received the cloud row.
Server-secret build scan pass 57 files, including 27 nested build artifacts, scanned against both available server secrets.
Physical devices not tested here Owned by #4 and #8.

The original run recorded screenshots named output/playwright/jazz2-alpha53-local-offline-multitab.png and output/playwright/jazz2-alpha53-cloud-second-client.png; those artifacts are not present in this checkout, so they are not cited as retained evidence here.

Compatibility findings

  1. Deno's npm layout puts jazz-wasm under node_modules/.deno. Jazz resolves the WASM to an absolute path, while Vite initially allows only the nested spike root. The browser received HTTP 403 until the Vite configuration allowed the repository root. This fix is required for the nested Astro prototype too.
  2. With the raw vendor command on a clean first local run, Jazz writes VITE_JAZZ_APP_ID into the spike's ignored .env. Vite restarts while starting and Deno/Vite 8 failed with Listen method has been called more than once without closing. A rerun started in 102 ms. The lofi task runner now pre-seeds a stable internal app ID; a clean wrapped first run started in 103 ms without a restart. The raw failure is retained because the integrated prototype must keep this edge.
  3. A Vite configuration restart can change the generated local server URL while a persistent browser broker is alive. Jazz then raised IncompatibleBrowserBrokerConfigurationError and required a page reload. The prototype must keep the runtime singleton stable and make reload recovery explicit.
  4. The plugin's TTY-only local server banner prints its generated admin secret. lofi cannot expose that banner unchanged under DX-ENV-01.
  5. BrowserAuthSecretStore uses localStorage and documents a non-atomic check-then-write on the first concurrent visit. First-run multi-tab identity creation needs lofi coordination or a documented limitation.
  6. The browser console emitted Jazz WASM CatalogueWriteDenied warnings while both local and global application-row waits succeeded. This is non-blocking for the baseline but must stay visible during #5/#6; lofi must not suppress it without proving that it is harmless catalogue traffic.

Runtime topology and transport

The exact tarball's persistent-browser path uses a SharedWorker broker to elect one leader tab. The leader owns the module worker that runs the persistent WASM/OPFS path; follower tabs attach through message ports. The upstream application-data transport is a Rust-owned WebSocket opened by the runtime. The package's sync-transport.js explicitly says the earlier HTTP/SSE path was removed. Schema publication by the development plugin remains an HTTP control-plane operation and is not the application-data sync transport.

Public API inventory

Area Exact alpha.53 surface M1 interpretation
Schema schema.table, scalar/json/enum/ref/array columns, defineSchema, defineApp Directly supported; keep the vendor schema in schema.ts.
Query typed where, select, include, orderBy, limit, offset, graph hops/gather; Db.all, one, subscribeAll Directly supported; reactive binding must reconcile subscription deltas.
Mutation insert, restore, upsert, update, delete, batch, transaction, local/global WriteHandle waits Directly supported; lofi adapts status/error presentation.
Migration typed defineMigration, add/drop/rename operations and tables; CLI create/push workflow Supported but not exercised by this minimal schema; author workflow remains post-M1.
Permissions typed definePermissions, per-operation always/never/where, creator, relation, session and allowed-to conditions Directly supported; the control scopes rows to the local-first identity.
Identity namespaced BrowserAuthSecretStore; local secret, JWT, cookie session; auth state/listener and local-first proof Local-first identity directly supported; lofi owns first-run coordination and wording.
Recovery phrase RecoveryPhrase.fromSecret / toSecret with typed validation errors Direct bearer-secret conversion; UX/security validation belongs to #8.
Passkey backup BrowserPasskeyBackup.backup / restore, explicit RP ID, typed errors Direct backup/restore primitive, not conventional login; device validation belongs to #8.
Persistence persistent or memory driver, OPFS database name, local durability wait, storage deletion Persistent path supported; capability/termination/device matrix belongs to #4.
Sync control server URL, per-write global wait, disconnect / reconnect Core supported; connection-state claims remain unavailable.

Desired lofi surface disposition

Desired lofi surface Disposition Basis
Application schema in schema.ts directly supported Jazz's typed relational schema is already concise.
One runtime per browser realm adapted Cache one Jazz Db and own HMR disposal outside product UI.
Reactive list/query binding adapted Wrap subscribeAll and reconcile deltas for framework identity stability.
Local create/update/delete directly supported Synchronous mutations plus local durability waits.
Retained/global write status directly supported WriteHandle.wait at local and global tiers.
Storage capability and boot diagnosis instrumented Browser capability probes plus persistent-driver success/failure.
Sync configured state directly supported Complete validated public configuration pair.
Live sync connection state unavailable No public state/listener in this pin.
Pending write count unavailable No public pending-operation API in this pin.
Mutation rejection UX adapted onMutationError and rejected waits feed lofi diagnostics.
Local-first recovery phrase adapted Direct primitive; lofi must provide bearer-secret warnings.
Passkey backup/restore adapted Direct primitive; lofi must stabilize RP ID and device UX.
PWA shell/update lifecycle outside Jazz Astro/service-worker layer owns application assets and updates.

Observability inventory

Claim Public alpha.53 evidence Decision
Persistent storage requested DbConfig.driver: { type: "persistent" } Report requested mode and boot failure honestly.
Local durability confirmed WriteHandle.wait({ tier: "local" }) Report per-write confirmation.
Cloud durability confirmed WriteHandle.wait({ tier: "global" }) Report per-write confirmation.
Sync configured Presence of a validated server URL Report configured, never infer connected.
Manual offline control Public Db.disconnect() / Db.reconnect() Expose only for tests/diagnostics; follower restrictions require care.
Auth state Db.getAuthState() / Db.onAuthChanged() Suitable for truthful identity wording.
Mutation rejection Db.onMutationError() and rejected write waits Surface impact and action.
Live transport connection No public listener/state found Do not claim connected/disconnected from configuration.
Pending operation count No public API found State unavailable; do not estimate.
Active subscriptions Internal diagnostic API only May guard development tests, not product UI.

Bundle observation

The control build emitted a 9,799.43 kB raw Jazz WASM asset (3,134.95 kB gzip). M1 does not set a bundle budget, but #4 must measure installed cold-start/offline behavior rather than assuming the asset is harmless.

Gate decision

Go for the Preact and Astro/Deno integration spikes using this pin and the lofi-owned runtime edge. The edge already prevents the clean-first-run restart and preserves the Vite filesystem allowance. Do not graduate M1 until the integrated runtime owns singleton/HMR lifecycle and the public command avoids the vendor secret-printing banner. #4 and #8 remain independent gates for physical-device durability and recovery semantics.