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 rendered
1# Jazz 2.0.0-alpha.53 baseline evidence23Date: 2026-07-15\4Repository commit under test: `00c7f18`\5Host: macOS 26.5.1 (25F80), Apple silicon\6Runtime: Deno 2.9.0, V8 14.9.207.2, TypeScript 6.0.3\7Browser: Playwright Headless Chrome 150.0.0.0\8Package: `jazz-tools@2.0.0-alpha.53`\9Tarball SHA-1: `d8e6f902498c13234da9cf02e530880bab183c4e`\10Tarball integrity:11`sha512-GSAt9axXPaZY3g0bZxEPeAmq/q4IWfJRdVMWmNYxLnaKzFHD3vYQf1y4VK9/ZAGCdabcxS8Im3dreISRHXkHSg==`1213The npm tarball and `deno.lock` agree on version and integrity. The package does not publish a14`gitHead`. Jazz's `main` branch at `3384730e1c4509524b38c52fc9102fc6753411aa` was inspected only for15context and is not represented as the source commit for this package.1617## Repeatable commands1819```sh20deno check spikes/jazz2-baseline/*.ts spikes/jazz2-baseline/src/*.ts21deno task spike:jazz2:build22deno task spike:jazz2:dev23deno task spike:jazz2:cloud24deno task check:secrets25```2627Browser procedure (using `playwright-cli`; equivalent DevTools actions are acceptable):28291. Start `deno task spike:jazz2:dev --port 4177 --strictPort`, open the printed URL, and snapshot.302. Add `retained-alpha53-control`; wait until the UI says `confirmed local`; reload and verify the31 row remains.323. Get the row ID from `li[data-note-id]` and call33 `globalThis.__LOFI_JAZZ2_PROBE__.update(id, "after-update")`; verify the subscribed list and34 durability state change, then reload again.354. Open a second tab at the same origin, add `multitab-follower-write`, and verify it appears in36 both tabs.375. Set the browser context offline, add `offline-local-write`, wait for local confirmation, restore38 the network, and verify both tabs converge without reloading.396. Start `deno task spike:jazz2:cloud --port 4178 --strictPort`, add `cloud-global-alpha53`, and40 wait until the UI says `global write confirmed`.417. Save browser storage state to ignored `.playwright-cli/cloud-identity-state.json`, load it in a42 fresh browser context, open the cloud URL, and verify the cloud row downloads. The state file43 contains the bearer identity and must never be committed or printed.4445The cloud task publishes the spike schema to the configured Jazz application. It projected only the46public app ID and server URL to the client. The test retained one note in that application; no47credential value is recorded here.4849## Results5051| Control | Result | Evidence |52| ------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------- |53| Deno type-check | pass | Exact npm pin resolved through `deno.json` and `deno.lock`. |54| Deno production build | pass | Vite transformed 371 modules without a user-facing Node command. |55| Persistent-driver boot | pass after compatibility fix | Chrome reported OPFS, SharedWorker, MessageChannel, and Web Locks; Jazz opened its persistent driver. |56| Immediate local write | pass | Subscription rendered the row before network confirmation. |57| Query and update | pass | `subscribeAll` returned the row; `Db.update` changed it, reached local durability, and survived reload. |58| Local durability | pass | `WriteResult.wait({ tier: "local" })` resolved. |59| Reload retention | pass | The row remained after a full page reload. |60| Offline write | pass | Browser network state was offline; a row rendered and reached local durability. |61| Multi-tab | pass | A second tab wrote through the follower path and both tabs received two rows. |62| Managed-cloud durability | pass | `WriteResult.wait({ tier: "global" })` resolved against the configured Jazz server. |63| Clean second cloud client | pass | A second browser context with the same restored identity and no copied OPFS data received the cloud row. |64| Server-secret build scan | pass | 57 files, including 27 nested build artifacts, scanned against both available server secrets. |65| Physical devices | not tested here | Owned by #4 and #8. |6667The original run recorded screenshots named68`output/playwright/jazz2-alpha53-local-offline-multitab.png` and69`output/playwright/jazz2-alpha53-cloud-second-client.png`; those artifacts are not present in this70checkout, so they are not cited as retained evidence here.7172## Compatibility findings73741. Deno's npm layout puts `jazz-wasm` under `node_modules/.deno`. Jazz resolves the WASM to an75 absolute path, while Vite initially allows only the nested spike root. The browser received HTTP76 403 until the Vite configuration allowed the repository root. This fix is required for the nested77 Astro prototype too.782. With the raw vendor command on a clean first local run, Jazz writes `VITE_JAZZ_APP_ID` into the79 spike's ignored `.env`. Vite restarts while starting and Deno/Vite 8 failed with80 `Listen method has been called more than81 once without closing.` A rerun started in 102 ms. The82 lofi task runner now pre-seeds a stable internal app ID; a clean wrapped first run started in 10383 ms without a restart. The raw failure is retained because the integrated prototype must keep this84 edge.853. A Vite configuration restart can change the generated local server URL while a persistent browser86 broker is alive. Jazz then raised `IncompatibleBrowserBrokerConfigurationError` and required a87 page reload. The prototype must keep the runtime singleton stable and make reload recovery88 explicit.894. The plugin's TTY-only local server banner prints its generated admin secret. lofi cannot expose90 that banner unchanged under DX-ENV-01.915. `BrowserAuthSecretStore` uses localStorage and documents a non-atomic check-then-write on the92 first concurrent visit. First-run multi-tab identity creation needs lofi coordination or a93 documented limitation.946. The browser console emitted Jazz WASM `CatalogueWriteDenied` warnings while both local and global95 application-row waits succeeded. This is non-blocking for the baseline but must stay visible96 during #5/#6; lofi must not suppress it without proving that it is harmless catalogue traffic.9798## Runtime topology and transport99100The exact tarball's persistent-browser path uses a SharedWorker broker to elect one leader tab. The101leader owns the module worker that runs the persistent WASM/OPFS path; follower tabs attach through102message ports. The upstream application-data transport is a Rust-owned WebSocket opened by the103runtime. The package's `sync-transport.js` explicitly says the earlier HTTP/SSE path was removed.104Schema publication by the development plugin remains an HTTP control-plane operation and is not the105application-data sync transport.106107## Public API inventory108109| Area | Exact alpha.53 surface | M1 interpretation |110| --------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |111| Schema | `schema.table`, scalar/json/enum/ref/array columns, `defineSchema`, `defineApp` | Directly supported; keep the vendor schema in `schema.ts`. |112| Query | typed `where`, `select`, `include`, `orderBy`, `limit`, `offset`, graph hops/gather; `Db.all`, `one`, `subscribeAll` | Directly supported; reactive binding must reconcile subscription deltas. |113| Mutation | `insert`, `restore`, `upsert`, `update`, `delete`, batch, transaction, local/global `WriteHandle` waits | Directly supported; lofi adapts status/error presentation. |114| 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. |115| 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. |116| 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. |117| Recovery phrase | `RecoveryPhrase.fromSecret` / `toSecret` with typed validation errors | Direct bearer-secret conversion; UX/security validation belongs to #8. |118| Passkey backup | `BrowserPasskeyBackup.backup` / `restore`, explicit RP ID, typed errors | Direct backup/restore primitive, not conventional login; device validation belongs to #8. |119| Persistence | persistent or memory driver, OPFS database name, local durability wait, storage deletion | Persistent path supported; capability/termination/device matrix belongs to #4. |120| Sync control | server URL, per-write global wait, `disconnect` / `reconnect` | Core supported; connection-state claims remain unavailable. |121122## Desired lofi surface disposition123124| Desired lofi surface | Disposition | Basis |125| ------------------------------------- | ------------------ | -------------------------------------------------------------------------- |126| Application schema in `schema.ts` | directly supported | Jazz's typed relational schema is already concise. |127| One runtime per browser realm | adapted | Cache one Jazz `Db` and own HMR disposal outside product UI. |128| Reactive list/query binding | adapted | Wrap `subscribeAll` and reconcile deltas for framework identity stability. |129| Local create/update/delete | directly supported | Synchronous mutations plus local durability waits. |130| Retained/global write status | directly supported | `WriteHandle.wait` at `local` and `global` tiers. |131| Storage capability and boot diagnosis | instrumented | Browser capability probes plus persistent-driver success/failure. |132| Sync configured state | directly supported | Complete validated public configuration pair. |133| Live sync connection state | unavailable | No public state/listener in this pin. |134| Pending write count | unavailable | No public pending-operation API in this pin. |135| Mutation rejection UX | adapted | `onMutationError` and rejected waits feed lofi diagnostics. |136| Local-first recovery phrase | adapted | Direct primitive; lofi must provide bearer-secret warnings. |137| Passkey backup/restore | adapted | Direct primitive; lofi must stabilize RP ID and device UX. |138| PWA shell/update lifecycle | outside Jazz | Astro/service-worker layer owns application assets and updates. |139140## Observability inventory141142| Claim | Public alpha.53 evidence | Decision |143| ---------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------- |144| Persistent storage requested | `DbConfig.driver: { type: "persistent" }` | Report requested mode and boot failure honestly. |145| Local durability confirmed | `WriteHandle.wait({ tier: "local" })` | Report per-write confirmation. |146| Cloud durability confirmed | `WriteHandle.wait({ tier: "global" })` | Report per-write confirmation. |147| Sync configured | Presence of a validated server URL | Report `configured`, never infer `connected`. |148| Manual offline control | Public `Db.disconnect()` / `Db.reconnect()` | Expose only for tests/diagnostics; follower restrictions require care. |149| Auth state | `Db.getAuthState()` / `Db.onAuthChanged()` | Suitable for truthful identity wording. |150| Mutation rejection | `Db.onMutationError()` and rejected write waits | Surface impact and action. |151| Live transport connection | No public listener/state found | Do not claim connected/disconnected from configuration. |152| Pending operation count | No public API found | State unavailable; do not estimate. |153| Active subscriptions | Internal diagnostic API only | May guard development tests, not product UI. |154155## Bundle observation156157The control build emitted a 9,799.43 kB raw Jazz WASM asset (3,134.95 kB gzip). M1 does not set a158bundle budget, but #4 must measure installed cold-start/offline behavior rather than assuming the159asset is harmless.160161## Gate decision162163**Go** for the Preact and Astro/Deno integration spikes using this pin and the lofi-owned runtime164edge. The edge already prevents the clean-first-run restart and preserves the Vite filesystem165allowance. Do not graduate M1 until the integrated runtime owns singleton/HMR lifecycle and the166public command avoids the vendor secret-printing banner. #4 and #8 remain independent gates for167physical-device durability and recovery semantics.