- commit
- 110e0af90b9c9f607f7d95fcc29edc0b65126e59
- parent
- dd63d68d506f5de8e4106cae7766c66ce0d940ac
- author
- Dami <dami.etoile@gmail.com>
| M | CONTRIBUTING.md | 13 | +- |
| M | README.md | 233 | ++++++++++++++++++------------ |
| M | apps/ | 3 | +- |
| M | deno.json | 12 | ++ |
| A | docs/ | 89 | +++++++++++ |
| M | docs/ | 5 | +- |
| M | docs/ | 9 | +- |
| A | docs/ | 145 | +++++++++++++++++++ |
| A | docs/ | 20 | +++ |
| A | docs/ | 93 | ++++++++++++ |
| M | docs/ | 104 | +++++++------- |
| A | docs/ | 102 | +++++++++++++ |
| A | docs/ | 66 | ++++++++ |
| A | docs/ | 30 | ++++ |
| A | docs/ | 38 | +++++ |
| A | docs/ | 38 | +++++ |
| M | docs/ | 7 | +- |
| M | docs/ | 8 | +- |
| M | docs/ | 27 | ++- |
| A | docs/ | 73 | +++++++++ |
| A | docs/ | 107 | ++++++++++++++ |
| A | docs/ | 95 | ++++++++++++ |
| M | package/ | 6 | + |
| M | package/ | 4 | +- |
@@ -15,9 +15,16 @@ alpha data layer stays honest about what actually works.
1515 deno task check1616 ```1717 18-This runs formatting, linting, type checks, Astro checks, the deterministic test suite, the19-environment-contract check, and the secret-leak scan. A change is not ready for review until it20-passes locally. There are no other hidden global tools.18+This runs formatting, linting, and the deterministic test suite. Deno type-checks the imported19+application and runtime modules as part of the suite, which also includes the environment contract20+and secret-leak scan. A change is not ready for review until it passes locally. There are no hidden21+global tools.22+23+Before publishing, also inspect the package artifact:24+25+```sh26+deno task publish:dry --allow-dirty27+```2128 2229 ## Contract-driven changes2330 @@ -1,120 +1,165 @@
11 # @nzip/lofi22 3-**A local-first mobile PWA meta-framework for Deno.** Declare a schema, build Preact islands, ship4-an installable offline-capable app. The UI always hydrates from local data, identity never waits on5-a server, and the sync module is the only network surface you touch.3+**Build installable Deno web apps that open immediately, keep working offline, and sync when users4+choose.**65 7-Built on [Jazz 2](https://jazz.tools) (CRDT data + sync) · [Preact](https://preactjs.com) islands in8-[Astro](https://astro.build) · the [Deno](https://deno.com) toolchain.6+lofi generates an [Astro](https://astro.build) + [Preact](https://preactjs.com) app backed by7+durable local data. Users start with a private, on-device account—no sign-in or network required—and8+can add [Jazz](https://jazz.tools)-powered sync and recovery later without replacing that identity9+or rewriting product code.10+11+[Documentation](docs/README.md) · [JSR package](https://jsr.io/@nzip/lofi) ·12+[GitHub](https://github.com/FelineStateMachine/lofi) · [MIT License](LICENSE)913 1014 ## Quick start1115 16+Requires **Deno 2.9+**. It is the only global runtime you need.17+1218 ```sh1319 deno run -A jsr:@nzip/lofi/create my-app1420 cd my-app1521 deno task dev1622 ```1723 18-That's the whole onboarding. You now have an installable, offline-capable local-first PWA — make a19-write, reload it, go offline, and the UI keeps working because it reads from durable local storage,20-not the network.24+Open the URL printed by the development server. The starter is a small task app: add a task and25+reload the page to see that it remains in durable local storage. Once the page is open, disconnect26+the network and you can keep reading and writing data locally.27+28+The generated app starts in local-only mode. You do not need an account, a backend, or an `.env`29+file to begin building.30+31+Continue with the [generated-app guide](docs/getting-started.md) when you are ready to replace the32+task example with your own schema, permissions, hook, and UI.33+34+## What you get2135 22-Want sync and account backup from the start? Scaffold with `--sync`:36+- **Local-first data.** The UI reads from durable local storage instead of waiting on the network.37+ Storage failures are surfaced rather than silently falling back to memory.38+- **Identity from the first launch.** Each user begins with a private on-device account, even when39+ the app has no sync service configured.40+- **Optional sync and recovery.** Users can make that same account portable when you connect the app41+ to managed Jazz sync.42+- **A narrow authoring surface.** Product code owns the schema, permissions, configuration, Preact43+ islands, styles, and tests. lofi owns storage, identity, sync, lifecycle, and PWA plumbing.44+- **Local-first test helpers.** Playwright-backed fixtures cover offline writes, multiple clients,45+ convergence, and readiness without hand-timed sleeps.46+- **Explicit mobile support.** Unsupported browsers receive a clear explanation instead of a47+ partially working app that risks data loss.48+49+## Add sync and account recovery50+51+To provision sync while scaffolding:2352 2453 ```sh2554 deno run -A jsr:@nzip/lofi/create --sync my-app2655 ```2756 28-It provisions a managed Jazz app and writes the project's `.env`, so the app can offer synced,29-recoverable accounts immediately.30-31-> Requires **Deno 2.9+**, the only global runtime. With no `.env` the app runs local-only (stated32-> explicitly at boot). For managed sync + backup, run `deno task jazz:provision` (or scaffold with33-> `create --sync`) to generate a Jazz app and write the `.env` — or set the public `JAZZ_APP_ID` /34-> `JAZZ_SERVER_URL` pair by hand. Server-only secrets never reach client output.35-36-## What a generated project looks like37-38-A new project is split into two zones — what you own, and the runtime you don't touch:39-40-```text41-my-app/42-├── deno.json # tasks + pinned toolchain43-├── astro.config.ts44-├── public/ # manifest, service worker, icons45-├── src/46-│ ├── schema.ts # ← your data model47-│ ├── permissions.ts # ← your access policy48-│ ├── app.ts # ← your app configuration49-│ ├── pages/ # ← your Astro shell50-│ ├── islands/ # ← your Preact UI (starts as a minimal task list to replace)51-│ ├── styles/52-│ └── _lofi/ # runtime: storage, identity, sync, lifecycle, diagnostics — don't edit53-└── tests/ # your tests + the @nzip/lofi/testing contract57+For an existing generated project:58+59+```sh60+deno task jazz:provision5461 ```5562 56-You write `schema.ts`, `permissions.ts`, islands, and styles. Everything under `src/_lofi/` is the57-framework runtime; product work never needs to touch it.63+Provisioning creates a managed Jazz app and writes the public `JAZZ_APP_ID` and `JAZZ_SERVER_URL`64+configuration to the project's `.env`. Server-only credentials remain outside client output.5865 59-## Commands66+Once sync is configured, a user can choose to back up and sync their existing account, then recover67+it on another device with a 24-word recovery phrase. Enabling sync preserves data created while the68+app was local-only because the account identity does not change.69+70+## Where lofi fits71+72+lofi is designed for mobile web apps where offline operation is a requirement, not a best-effort73+enhancement. It is a good fit when you want Deno tooling, an Astro shell, Preact islands, and an74+opinionated boundary between product code and local-first infrastructure.75+76+Keep these current constraints in mind:77+78+- lofi is an early, pre-1.0 release.79+- The data layer is Jazz 2 alpha and deliberately pinned to a reviewed version.80+- The supported browser floors are Android Chrome 148+ and iOS Safari 16.4+.81+- Recovery is user-controlled: lofi does not retain recoverable account material on the server, so82+ the recovery phrase must be kept safe.6083 61-Every generated project ships this task surface (`deno task <name>`):84+## Project anatomy85+86+A generated project separates the files you own from the framework runtime:87+88+```mermaid89+flowchart TB90+ Root["my-app/"] --> Config["deno.json + astro.config.ts"]91+ Root --> Public["public/<br/>manifest · service worker · icons"]92+ Root --> Src["src/"]93+ Root --> Tests["tests/<br/>your tests + lofi testing contract"]94+95+ Src --> Model["schema.ts + permissions.ts<br/>your model and access policy"]96+ Src --> App["app.ts<br/>your application config"]97+ Src --> UI["pages + islands + styles<br/>your product experience"]98+ Src --> Runtime["_lofi/<br/>generated runtime—do not edit"]99+```62100 63-| Command | What it does |64-| --------------------------------------- | ------------------------------------------------------------- |65-| `dev` | Astro dev server; prints storage, identity, and sync state. |66-| `doctor` | Value-free readiness report — no secrets, no faked claims. |67-| `test` | Deterministic local-first tests (no hand-timed sleeps). |68-| `build` | Static production build into `dist/`. |69-| `preview` | Serves the production build locally. |70-| `deploy` / `deploy:create` | Host the static build on Deno Deploy. |71-| `jazz:provision` | Generate a managed Jazz app and write `.env` for sync/backup. |72-| `schema:validate` / `schema:deploy` | Validate and publish your Jazz schema. |73-| `migrations:create` / `migrations:push` | Author and push schema migrations. |101+Most product work stays in `schema.ts`, `permissions.ts`, `app.ts`, `pages/`, `islands/`, and102+`styles/`. Everything under `src/_lofi/` is generated runtime code.74103 75104 ## Testing local-first behavior76105 77-`@nzip/lofi/testing` provides Playwright-backed helpers for the parts that are genuinely hard to78-test — offline and multi-client behavior: a two-client fixture, concurrent-offline convergence,79-readiness waits without arbitrary sleeps, and value-free (secret-free) failure artifacts. Every80-project includes a worked example at `tests/convergence_e2e_test.ts`.81-82-## Why lofi83-84-- **Local-first without the plumbing.** Product code touches schema, config, islands, styles, and85- tests — never providers, workers, transports, or service-worker internals.86-- **Honest by construction.** Durable storage never silently degrades to memory; diagnostics only87- claim what the underlying APIs can actually observe; identity wording matches real custody and88- recovery semantics.89-- **Mobile PWA first.** Platform floors (Android Chrome 148+, iOS Safari 16.4+) are enforced boot90- gates, not advisory footnotes. Unsupported browsers get an explicit answer, never silent data91- loss.92-93-## Stack94-95-| Layer | Choice | Pinned at |96-| ---------- | --------------------------------------------- | ------------------- |97-| Data/sync | jazz-tools 2.0 alpha, OPFS | `2.0.0-alpha.53` |98-| UI runtime | Preact islands, `client:only`, thin adapter | Preact 10 / Astro 7 |99-| Shell | Astro, fully prerendered, static-through-Deno | Astro 7 |100-| Toolchain | Deno tasks + npm-compat | Deno 2.9 |101-102-Version pins are deliberate: the data layer is an alpha, so every bump is a reviewed decision.103-104-## Status105-106-Early release, pre-1.0. Identity is **local-first**: a new app opens on a private, on-device account107-with no sign-in and no network. When a managed Jazz app is configured (`deno task jazz:provision`),108-the user can elect to back up and sync that account and recover it from a 24-word **recovery109-phrase** — the same account, made portable. Electing to sync preserves everything created while110-local-only, because the account identity never changes. lofi holds no recoverable account material111-server-side: the phrase is the backup, so keeping it is the user's custody. (The passkey/PRF module112-in `src/_lofi/auth.ts` remains as an optional at-rest-encryption primitive; deriving the account113-_from_ a credential was removed because it cannot preserve a pre-existing local-only account's114-data.)115-116----117-118-This repository is also the framework's development monorepo: `package/` is the `@nzip/lofi` source119-and `apps/reference/` is the reference app the generator is validated against. See120-[CONTRIBUTING.md](CONTRIBUTING.md) and the [DevX contract](docs/devx-contract.md).106+`@nzip/lofi/testing` provides Playwright-backed helpers for behavior that is difficult to test with107+ordinary unit tests: two-client fixtures, concurrent offline writes, convergence, readiness waits,108+and secret-free failure artifacts.109+110+Every generated project includes a worked example at `tests/convergence_e2e_test.ts`.111+112+## Commands113+114+Every generated project exposes these tasks as `deno task <name>`.115+116+### Everyday development117+118+| Command | What it does |119+| --------- | ----------------------------------------------------------------- |120+| `dev` | Runs the Astro development server and prints runtime state. |121+| `doctor` | Checks readiness without printing configuration or secret values. |122+| `test` | Runs the deterministic local-first test suite. |123+| `build` | Creates a static production build in `dist/`. |124+| `preview` | Serves the production build locally. |125+126+### Sync, schema, and deployment127+128+| Command | What it does |129+| --------------------------------------- | ------------------------------------------------- |130+| `jazz:provision` | Creates a managed Jazz app and configures `.env`. |131+| `schema:validate` / `schema:deploy` | Validates and publishes the Jazz schema. |132+| `migrations:create` / `migrations:push` | Authors and pushes schema migrations. |133+| `deploy` / `deploy:create` | Hosts the static build on Deno Deploy. |134+135+## Stack and version policy136+137+| Layer | Choice | Pinned at |138+| ---------- | ------------------------------------------ | ------------------- |139+| Data/sync | Jazz 2, CRDTs, OPFS | `2.0.0-alpha.53` |140+| UI runtime | Preact islands with a thin adapter | Preact 10 / Astro 7 |141+| Shell | Fully prerendered Astro, statically hosted | Astro 7 |142+| Toolchain | Deno tasks and npm compatibility | Deno 2.9 |143+144+Version pins are deliberate. Because the data layer is an alpha, every upgrade is reviewed and145+validated before it becomes part of the generated project.146+147+## Identity and recovery model148+149+A new app opens immediately on a private, on-device account. When managed sync is configured, the150+user can back up and sync that same account and recover it elsewhere with the recovery phrase. The151+phrase is the portable backup; keeping it safe is the user's responsibility.152+153+See [Sync and recovery](docs/sync-and-recovery.md) for setup and shipping guidance, and154+[Local-first accounts: open now, back up later](docs/auth-identity.md) for the detailed account155+states, recovery guarantees, custody model, and optional passkey-based protection at rest.156+157+## Developing lofi itself158+159+This repository is the framework's development monorepo. `package/` contains the `@nzip/lofi`160+source, while `apps/reference/` is the reference app against which the generator is validated.161+162+See [CONTRIBUTING.md](https://github.com/FelineStateMachine/lofi/blob/main/CONTRIBUTING.md) for the163+repository workflow and the164+[DevX contract](https://github.com/FelineStateMachine/lofi/blob/main/docs/devx-contract.md) for the165+framework's testable promises and boundaries.@@ -9,7 +9,8 @@ export const referenceApp = {
99 // Identity is local-first: first boot opens a private, on-device account with1010 // no sign-in. When a managed Jazz app is configured (JAZZ_APP_ID / JAZZ_SERVER_URL —1111 // see `deno task jazz:provision`), the AccountGate island lets the user elect to12- // back up and sync it, and recover it from a phrase. See docs/auth-identity.md.12+ // back up and sync it, and recover it from a phrase. See the framework's13+ // `docs/sync-and-recovery.md` guide.1314 //1415 // Hostnames you have committed to keeping stable, for the optional device-credential1516 // primitive in `src/_lofi/auth.ts` (WebAuthn/PRF at-rest encryption). A credential@@ -37,6 +37,7 @@
3737 "jsxImportSource": "preact"3838 },3939 "tasks": {40+ "check": "deno fmt --check . && deno lint && deno task test:unit",4041 "test:unit": "deno test --allow-env=LOFI_UPDATE_SNAPSHOT,CI --allow-net=127.0.0.1 --allow-read --allow-write=. --allow-run tools package apps/reference/src/_lofi apps/reference/tests",4142 "test:update:create": "LOFI_UPDATE_SNAPSHOT=1 deno test --allow-env=LOFI_UPDATE_SNAPSHOT --allow-read --allow-write=. --allow-run package/create_core_test.ts",4243 "test": "deno task test:unit",@@ -57,6 +58,17 @@
5758 "include": [5859 "LICENSE",5960 "README.md",61+ "docs/README.md",62+ "docs/auth-identity.md",63+ "docs/auth.md",64+ "docs/data-and-ui.md",65+ "docs/deployment.md",66+ "docs/getting-started.md",67+ "docs/permissions.md",68+ "docs/reference",69+ "docs/sync-and-recovery.md",70+ "docs/testing.md",71+ "docs/troubleshooting.md",6072 "apps/reference/astro.config.ts",6173 "apps/reference/deno.json",6274 "apps/reference/public",@@ -0,0 +1,89 @@
1+# lofi developer documentation2+3+These guides are for developers building an application from the `@nzip/lofi` generated template.4+They describe the checked-out package version and its generated project—not the older prototype5+sketches under `docs/spikes/`.6+7+## Start here8+9+```mermaid10+flowchart LR11+ A["Create and run"] --> B["Model data and permissions"]12+ B --> C["Build the Preact UI"]13+ C --> D["Add sync if needed"]14+ D --> E["Test local-first behavior"]15+ E --> F["Build and deploy"]16+ A -. "blocked" .-> G["Troubleshoot"]17+ B -. "blocked" .-> G18+ D -. "blocked" .-> G19+ E -. "blocked" .-> G20+```21+22+1. [Getting started](getting-started.md) — scaffold an app, verify local persistence, and identify23+ the files you are expected to change.24+2. [Data and UI](data-and-ui.md) — connect a Jazz table to a typed Preact hook and island.25+3. [Permissions](permissions.md) — understand and change the starter's owner-only access policy.26+4. [Sync and recovery](sync-and-recovery.md) — provision managed sync and understand the account27+ lifecycle.28+5. [Testing](testing.md) — run fast tests and the opt-in two-client offline convergence example.29+6. [Deployment](deployment.md) — build, preview, customize, and host the static PWA.30+7. [Troubleshooting](troubleshooting.md) — diagnose common environment, browser, build, and test31+ failures.32+33+The root [README](../README.md) provides the shortest product overview and command summary.34+35+## Reference36+37+- [Commands](reference/commands.md)38+- [Configuration](reference/configuration.md)39+- [Generated project layout](reference/project-layout.md)40+- [Identity and recovery model](auth-identity.md)41+- [Advanced device-auth primitive](auth.md)42+43+## The author boundary44+45+Generated projects intentionally divide application code from framework runtime code:46+47+```mermaid48+flowchart TB49+ subgraph Author["You own"]50+ Schema["schema.ts"]51+ Permissions["permissions.ts"]52+ App["app.ts"]53+ UI["pages, islands, styles"]54+ Public["public assets"]55+ Tests["tests"]56+ end57+58+ subgraph Framework["lofi owns"]59+ Runtime["src/_lofi"]60+ Storage["storage and subscriptions"]61+ Identity["identity and recovery"]62+ Lifecycle["sync, PWA, lifecycle"]63+ end64+65+ UI --> Runtime66+ Runtime --> Storage67+ Runtime --> Identity68+ Runtime --> Lifecycle69+```70+71+- Change `src/schema.ts`, `src/permissions.ts`, `src/app.ts`, `src/pages/`, `src/islands/`, and72+ `src/styles/`.73+- Treat `src/_lofi/` as generated runtime code. Application hooks may import its documented seams in74+ the same way as the starter, but ordinary product work should not edit those files.75+- Change files under `public/` when customizing the icon, manifest, or service worker assets.76+- Keep application tests under `tests/`.77+78+## Framework contributor material79+80+The following documents explain why lofi behaves as it does. They are useful when changing the81+framework, but they are not application tutorials:82+83+- [Developer-experience contract](https://github.com/FelineStateMachine/lofi/blob/main/docs/devx-contract.md)84+- [Historical prototype seed](https://github.com/FelineStateMachine/lofi/blob/main/docs/seed.md)85+- [`docs/spikes/`](https://github.com/FelineStateMachine/lofi/tree/main/docs/spikes) and retained86+ evidence87+88+Spike evidence records the state of an experiment at a specific commit and package pin. Preserve it89+as historical evidence; do not update it to look like the current generated app.@@ -1,5 +1,8 @@
11 # Local-first accounts: open now, back up later22 3+This document explains the account and custody model. For project setup and a shipping checklist,4+see [Sync and recovery](sync-and-recovery.md).5+36 lofi's identity model is **local-first**. A new app opens immediately on a private, on-device47 account — no sign-in, no ceremony, nothing sent anywhere. From there the user can _elect_ to back up58 and sync that account, and recover it on another device. Electing to sync preserves everything@@ -107,4 +110,4 @@ it expires. `.env` holds the server-only secrets and is git-ignored.
107110 derive a credential-bound key (PRF) to encrypt data — including the account secret — **at rest**. It108111 is feature-detected and never faked. It is _not_ the account identity (deriving the account from a109112 credential was removed, since a derived key is a different account and cannot carry local-only data110-forward). See [docs/auth.md](auth.md).113+forward). See [Advanced device auth](auth.md).@@ -3,11 +3,16 @@
33 Status: **primitive validated in reference source; `./auth` subpath deferred to a later milestone**\44 Scope: **device-local WebAuthn + PRF at-rest key derivation**55 6+> **Advanced/internal seam.** Generated applications do not expose this as a supported package7+> subpath, and normal product work should not edit `src/_lofi/auth.ts`. Use this document only when8+> deliberately evaluating the optional device-credential primitive. Account sync and recovery are9+> documented separately in [Sync and recovery](sync-and-recovery.md).10+611 lofi device auth is a small, honest **primitive**, not a mandated identity or recovery scheme.712 Local-first identity is device-local and cryptographic — there is no central store to authenticate813 against — so this module does exactly three things: enroll a device passkey, authenticate with it,9-and derive a credential-bound key to encrypt data **at rest**. Everything else (accounts, recovery,10-multi-device) is left to the application, deliberately.14+and derive a credential-bound key to encrypt data **at rest**. The generated session runtime handles15+accounts, recovery phrases, and optional multi-device sync separately.1116 1217 The runtime lives at `apps/reference/src/_lofi/auth.ts`.1318 @@ -0,0 +1,145 @@
1+# Data and UI2+3+The generated task list demonstrates lofi's main authoring pattern:4+5+```mermaid6+flowchart LR7+ Schema["schema.ts<br/>table and row types"] --> Permissions["permissions.ts<br/>read and write policy"]8+ Schema --> Hook["domain hook<br/>useTasks"]9+ Runtime["src/_lofi runtime<br/>storage and subscriptions"] --> Hook10+ Hook --> Island["Preact island<br/>product UI"]11+ Island -- "domain mutation" --> Hook12+ Hook -- "insert, update, delete" --> Runtime13+ Runtime -- "typed snapshot" --> Hook14+```15+16+The runtime owns the database, durable storage, subscriptions, and sync lifecycle. The domain hook17+selects a table and exposes product-specific reads and mutations to the UI.18+19+## 1. Declare a table20+21+`src/schema.ts` uses the pinned Jazz schema API directly:22+23+```ts24+import { schema as s } from "jazz-tools";25+26+const schema = {27+ notes: s.table({28+ title: s.string(),29+ body: s.string(),30+ archived: s.boolean(),31+ createdAt: s.timestamp(),32+ }),33+};34+35+type AppSchema = s.Schema<typeof schema>;36+export const app: s.App<AppSchema> = s.defineApp(schema);37+```38+39+Keep table names stable once data has shipped. A schema change can require a migration; validate the40+schema and review the Jazz CLI output before deploying it.41+42+## 2. Define permissions for the table43+44+Every new table needs an explicit policy in `src/permissions.ts`. The starter policy lets a user45+insert rows and limits reads, updates, and deletes to the row creator. See46+[Permissions](permissions.md) before changing that boundary.47+48+## 3. Bind the table to a typed hook49+50+The generated `src/islands/use-tasks.ts` is the canonical example. Its important pieces are:51+52+```ts53+import { schema as s } from "jazz-tools";54+import { referenceApp } from "../app.ts";55+import { getRuntime } from "../_lofi/runtime.ts";56+57+const notesTable = referenceApp.schema.notes;58+export type Note = s.RowOf<typeof notesTable>;59+```60+61+`s.RowOf` derives the row type from the schema, including its `id`. Do not duplicate persisted row62+types by hand.63+64+When the hook connects, it asks the runtime for a shared store:65+66+```ts67+const runtime = await getRuntime();68+const store = runtime.store(notesTable);69+```70+71+Repeated calls for the same table return the same store. Multiple consumers therefore share one72+underlying Jazz subscription.73+74+Use the generated hook's subscription and cleanup structure rather than opening a raw Jazz75+subscription in a component. It handles unmounting and reconnects after a user enables sync or76+restores another account.77+78+## 4. Expose domain mutations79+80+The table store provides three mutations:81+82+```ts83+await store.insert({ title, body, archived: false, createdAt: new Date() });84+await store.update(noteId, { archived: true });85+await store.delete(noteId);86+```87+88+Wrap these in domain verbs such as `createNote`, `archiveNote`, and `deleteNote`. Product components89+should not need to know which table or runtime implements them.90+91+Each mutation waits for local durability before its promise resolves. When the user has elected to92+sync, the store also tracks global durability in the background.93+94+```mermaid95+sequenceDiagram96+ participant UI as Preact island97+ participant Hook as Domain hook98+ participant Store as Table store99+ participant Disk as OPFS100+ participant Sync as Managed sync101+102+ UI->>Hook: createNote(...)103+ Hook->>Store: insert(...)104+ Store->>Disk: persist mutation105+ Disk-->>Store: local durability106+ Store-->>Hook: mutation resolves107+ Hook-->>UI: update status108+ opt User elected to sync109+ Store->>Sync: replicate mutation110+ Sync-->>Store: global durability111+ end112+```113+114+## 5. Render every state honestly115+116+A table snapshot contains:117+118+| Field | Values | Meaning |119+| ------------ | ----------------------------------- | ------------------------------- |120+| `status` | `loading`, `ready`, `error` | Subscription/read state |121+| `rows` | Typed row array | Current local view |122+| `durability` | `none`, `local`, `global`, `failed` | Most recent mutation durability |123+| `error` | A message or `null` | Read or mutation failure |124+125+Render loading and failure states rather than treating an empty row array as proof that loading126+finished. A locally durable write is safe in device storage; a globally durable write has also127+reached configured sync.128+129+## Schema changes and migrations130+131+Generated projects provide these Jazz CLI tasks:132+133+```sh134+deno task schema:validate135+deno task migrations:create136+deno task migrations:push137+deno task schema:deploy138+```139+140+Use `schema:validate` while editing. When a change affects existing data, use the migration command141+to create the migration under `src/migrations/`, review the generated change, and test it against142+non-sensitive fixture data before pushing or deploying it.143+144+`migrations:push` and `schema:deploy` use the managed Jazz configuration in `.env`. Never paste its145+server-only secrets into source, client code, screenshots, or issue text.@@ -0,0 +1,20 @@
1+# Decision records2+3+Framework changes that alter a documented product promise should retain a short decision record in4+this directory. Historical M1/M2 decisions predate this index and remain represented by the5+developer-experience contract, spike evidence, issues, and pull requests.6+7+Name new records `NNNN-short-title.md` and include:8+9+1. **Question** — the decision being made.10+2. **Contract IDs** — promises affected in `docs/devx-contract.md`.11+3. **Exact versions** — package, runtime, browser, OS, and commit pins.12+4. **Control** — the comparison or vendor baseline.13+5. **Procedure** — repeatable commands and observable steps.14+6. **Evidence** — retained, non-secret artifacts.15+7. **Decision** — adopt, revise, defer, or reject.16+8. **Contract delta** — what changed in the product promise.17+9. **Follow-up** — remaining work with an owner or issue.18+19+A negative result is a valid decision. Do not replace evidence with a search summary, and never20+record environment values, account secrets, recovery phrases, or sensitive user content.@@ -0,0 +1,93 @@
1+# Building and deploying a lofi app2+3+lofi produces a static PWA. The production build contains prerendered HTML, JavaScript, the Jazz4+WASM/runtime assets, the web manifest, and a revisioned service worker.5+6+```mermaid7+flowchart LR8+ Source["Application source"] --> Doctor["doctor + test"]9+ Doctor --> Build["deno task build"]10+ Build --> Fingerprint["source fingerprint"]11+ Build --> Precache["service-worker precache"]12+ Build --> Scan{"secret scan passes?"}13+ Scan -- "no" --> Stop["Stop and remove leak"]14+ Scan -- "yes" --> Dist["dist/"]15+ Dist --> Preview["Local preview"]16+ Dist --> Deno["Deno Deploy"]17+ Dist --> Static["Other static host"]18+```19+20+## Build and preview21+22+```sh23+deno task build24+deno task preview25+```26+27+`build` writes `dist/`, records a source fingerprint in `dist/lofi-build.json`, generates the28+precache list, and scans for server-secret values. `preview` refuses to start when the build29+identity is missing or invalid.30+31+To use another preview port:32+33+```sh34+deno task preview --port 417335+```36+37+## Configure the public application surface38+39+Before deploying, review:40+41+- `src/app.ts` — name, database namespace, stable credential origins, and repository URL;42+- `public/manifest.webmanifest` — installed name, icons, colors, start URL, and display mode;43+- `public/favicon.svg` and any added icon files;44+- page titles, descriptions, and starter copy;45+- `.env` — either no public Jazz pair for local-only mode or a complete pair for optional sync.46+47+Run `deno task doctor` and `deno task test` before the production build.48+49+## Deno Deploy50+51+Create the static application once:52+53+```sh54+deno task deploy:create --org <org> --app <app>55+```56+57+For later releases:58+59+```sh60+deno task deploy61+```62+63+Both tasks build first and deploy `dist/` as the static root.64+65+## Other static hosts66+67+Upload the contents of `dist/` to any host that can:68+69+- serve `index.html` at the application root;70+- preserve the manifest and WASM content types;71+- serve the application over HTTPS;72+- keep the service worker at the intended scope;73+- fall back to the appropriate prerendered HTML for application routes.74+75+Do not run a server-side Jazz credential in the static host or expose `JAZZ_ADMIN_SECRET` or76+`BACKEND_SECRET` as public environment variables.77+78+## Stable origins matter79+80+Durable storage and service workers require a secure context outside localhost. WebAuthn credentials81+also bind to the hostname. Choose the permanent production hostname before relying on device82+credentials, add it to `credentialOrigins`, and avoid redirect or preview URLs that change between83+deployments.84+85+## Release verification86+87+On the deployed HTTPS URL:88+89+1. Confirm the device capability panel passes.90+2. Add data, reload, and restart the browser.91+3. Install the PWA and perform an offline cold start.92+4. If sync is configured, opt in with a throwaway account and verify another device can recover it.93+5. Inspect the built application for the expected version/source fingerprint.@@ -1,14 +1,19 @@
11 # lofi developer-experience contract22 3-Status: **v0 validated through the M2 source layers; combined promotion review in progress**\4-Scope: **M0 contract, M1 feasibility, and M2 DevX graduation**\3+Status: **framework contributor contract with retained M1/M2 history**\4+Scope: **product promises, M1 feasibility, and M2 DevX graduation**\55 Last reviewed: **2026-07-15**66 7+> Application developers should start with the [developer documentation](README.md). Milestone,8+> issue, and evidence language below is retained for framework contributors and should not be read9+> as a generated-project tutorial.10+711 This is the product contract for lofi. Implementation choices are provisional until they satisfy812 these promises. Each M1 spike must cite the contract IDs it tested, retain repeatable evidence, and913 record a contract delta even when the result does not change the contract.1014 11-The older `init.md` is research input, not an authoritative description of Jazz or browser behavior.15+The older [prototype seed](seed.md) is research input, not an authoritative description of Jazz or16+browser behavior.1217 1318 ## Status and merge rules1419 @@ -26,8 +31,7 @@ honesty of the graduated reference application.
2631 2732 ### North-star generated-project journey2833 29-This is the product experience validated from M2 source. The registry command remains unavailable30-until the first explicitly authorized publish and registry-backed smoke.34+This is the current generated-project product journey:3135 3236 ```sh3337 deno run -A jsr:@nzip/lofi/create my-app@@ -35,10 +39,9 @@ cd my-app
3539 deno task dev3640 ```3741 38-The JSR surface is one package, `@nzip/lofi`, with one version and publish operation. M2 validates39-the command subpaths plus `./testing`; remaining runtime subpaths such as `./core`, `./sync`,40-`./auth`, `./ui`, and `./pwa` graduate only when M4 proves their seams through another application.41-See [ADR 0005](decisions/0005-publish-one-nzip-lofi-package.md).42+The JSR surface is one package, `@nzip/lofi`, with one version and publish operation. Its supported43+public surface is the generator, command subpaths, and `./testing`. Runtime modules remain generated44+under `src/_lofi/` until another application proves a stable package seam.4245 4346 From there the developer makes a retained local write, reloads it, works offline, runs checks,4447 builds, previews, and opens the same stable secure origin on a physical device.@@ -62,17 +65,17 @@ required global runtime for a generated application.
6265 6366 ## End-to-end journey contract6467 65-| Step | Developer action | Required result | Representative failure and recovery |66-| ------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |67-| Create | Run the create command with a project name. | A complete directory is produced without prompts and the next two commands are printed. | Existing/non-empty destination names the conflict and suggests a new path; no overwrite occurs. |68-| Configure | Start with no `.env`, or copy `.env.example` and provide the public cloud pair. | No config selects explicit local-only mode; a complete pair selects cloud mode. | Partial/invalid config stops boot, states that sync cannot start, and says which name to set or remove. |69-| Develop | Run `deno task dev`. | One local URL, storage mode, identity state, sync mode, and PWA readiness are printed. | Unsupported durable storage stops or requires explicit ephemeral opt-in; it never silently degrades. |70-| First write | Use the starter UI to create data. | UI updates without awaiting network; data survives reload or runtime recreation. | A failed durable write is visible in UI and diagnostics with impact and remediation. |71-| Reload/HMR | Reload and edit an island five times. | Identity and local data remain; client/subscription counts do not grow. | Duplicate runtime resources fail a development assertion with the responsible subsystem named. |72-| Offline | Disable network, cold-start, read, and edit. | Local behavior continues; returning online makes no unsupported connection-state claim. | Unsupported or failed sync remains visible; convergence waits for an explicit observable test seam. |73-| Check/test | Run `deno task check` and `deno task test`. | Static checks and deterministic local-first tests pass without hidden global tools in generated apps. | Failure prints the failing check and preserves browser/log evidence. |74-| Build/preview | Run `deno task build`, then `deno task preview`. | Production output is served through the supported Deno path. | npm-compat or adapter failure names the internal fallback; no undocumented user-facing Node command appears. |75-| Device | Open the printed stable HTTPS origin on a phone. | Secure-context, storage, install, and passkey capability are diagnosed. | An unstable relying-party ID is rejected before passkey creation and the stable-origin action is named. |68+| Step | Developer action | Required result | Representative failure and recovery |69+| ------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |70+| Create | Run the create command with a project name. | A complete directory is produced without prompts and the next two commands are printed. | Existing/non-empty destination names the conflict and suggests a new path; no overwrite occurs. |71+| Configure | Start with no `.env`, or copy `.env.example` and provide the public cloud pair. | No config selects explicit local-only mode; a complete pair selects cloud mode. | Partial/invalid config stops boot, states that sync cannot start, and says which name to set or remove. |72+| Develop | Run `deno task dev`. | One local URL, storage mode, identity state, sync mode, and PWA readiness are printed. | Unsupported durable storage stops or requires explicit ephemeral opt-in; it never silently degrades. |73+| First write | Use the starter UI to create data. | UI updates without awaiting network; data survives reload or runtime recreation. | A failed durable write is visible in UI and diagnostics with impact and remediation. |74+| Reload/HMR | Reload and edit an island five times. | Identity and local data remain; client/subscription counts do not grow. | Duplicate runtime resources fail a development assertion with the responsible subsystem named. |75+| Offline | Disable network, cold-start, read, and edit. | Local behavior continues; returning online makes no unsupported connection-state claim. | Unsupported or failed sync remains visible; convergence waits for an explicit observable test seam. |76+| Diagnose/test | Run `deno task doctor` and `deno task test`. | Readiness checks and deterministic local-first tests pass without hidden global tools in generated apps. | Failure names the blocked check and preserves browser/log evidence. |77+| Build/preview | Run `deno task build`, then `deno task preview`. | Production output is served through the supported Deno path. | npm-compat or adapter failure names the internal fallback; no undocumented user-facing Node command appears. |78+| Device | Open the printed stable HTTPS origin on a phone. | Secure-context, storage, install, and passkey capability are diagnosed. | An unstable relying-party ID is rejected before passkey creation and the stable-origin action is named. |7679 7780 ## Measurement protocol7881 @@ -92,41 +95,39 @@ required global runtime for a generated application.
9295 9396 ## Measurable promises9497 95-| ID | Promise | v0 budget or condition | Evidence owner | M1 gate | Status |96-| --------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ------- | --------- |97-| DX-CREATE-01 | Named creation is non-interactive. | Deno is the only required global runtime; zero prompts; uncached time reported. | M2 source; cold registry pending | no | proposed |98-| DX-CMD-01 | The public command surface is small. | create, dev, doctor, check, test, build, preview only. | #2 and #6 | yes | validated |99-| DX-TTFW-01 | Generated create-to-retained-write is fast. | At most three shell commands and two minutes on the recorded machine/network. | M2 source; cold registry pending | no | proposed |100-| DX-PROTOTYPE-01 | The M1 checkout reaches a retained write quickly. | `deno task dev` to retained write within 60 seconds after cached setup. | integrated M1 prototype | yes | validated |101-| DX-START-01 | Cached development startup feels immediate. | Warm median at most 2 seconds; slowest at most 5 seconds across five samples. | #6 | yes | validated |102-| DX-HMR-01 | UI edits retain state and runtime cardinality. | Median feedback at most 300 ms; one client and one vendor subscription per shared query after five edits. | #5 and #6 | yes | validated |103-| DX-AUTHOR-01 | Product edits avoid framework plumbing. | The integrated task changes only schema, app config, page/island, style, or test files. | integrated prototype | yes | validated |104-| DX-LEAK-01 | Runtime machinery stays outside product UI. | No provider, raw client, worker, transport URL, Workbox config, or browser branch in product UI. | #5, #6, integration | yes | validated |105-| DX-LOCAL-01 | Local work does not await network/auth round trips. | Subscribed UI reflects an offline write in the same event turn or next render; reload retains it. | #7 | yes | validated |106-| DX-DUR-01 | Durable mode never silently becomes ephemeral. | Boot reports durable, unsupported, or explicitly opted-in memory mode. | #4 and #7 | yes | validated |107-| DX-SYNC-01 | Application-data transport has one lofi-owned surface. | App config selects a named adapter; product UI never constructs peers or transports. | #7 and integration | yes | validated |108-| DX-OBS-01 | Diagnostics expose only truthful signals. | Configured state and per-write durability map to public APIs; unavailable transport detail is named. | #7 | yes | validated |109-| DX-ENV-01 | Environment handling is safe by construction. | Real env ignored; allowlisted loader; server values absent from client projection and built output. | #3, #7, final build | yes | validated |110-| DX-ERROR-01 | Failures state capability, impact, and action. | No secret values; common config recovery is one edit plus rerun. | #3, #4, #6 | yes | validated |111-| DX-AUTH-01 | Identity wording matches custody/recovery. | UI identifies the device-local key, blocks the rejected alpha passkey path, and defines any future phrase as an identity bearer secret rather than data backup. | #8 | yes | revised |112-| DX-DEVICE-01 | Device/auth tests use a stable, appropriately isolated origin. | M1 proves a stable HTTPS PWA scope for OPFS; installed-app RP-ID preservation and identity isolation remain required before any replacement passkey ceremony. | #4 and #8 | yes | revised |113-| DX-DEVICE-UX-01 | Device preview is one productized command. | Primary path prints stable URL, capability report, and remediation. | M3 | no | proposed |114-| DX-OFFLINE-01 | Installed production cold-start renders retained data offline. | Airplane-mode launch renders shell and data. | feasibility #4; full M3 | no | proposed |115-| DX-BUILD-01 | Development and production share the Deno command contract. | `deno task build` and `preview`; no undocumented Node command. | #6 | yes | validated |116-| DX-TEST-01 | Local-first tests avoid hand-timed sleeps. | Readiness-based offline/two-client primitives. | #7 and M2 Layer 3 | no | validated |98+| ID | Promise | v0 budget or condition | Evidence owner | M1 gate | Status |99+| --------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ------- | --------- |100+| DX-CREATE-01 | Named creation is non-interactive. | Deno is the only required global runtime; zero prompts; uncached time reported. | M2 source and release smoke | no | validated |101+| DX-CMD-01 | The public command surface is small. | create, dev, doctor, test, build, preview, plus explicit sync/schema/deploy tasks. | #2, #6, and M2 | yes | revised |102+| DX-TTFW-01 | Generated create-to-retained-write is fast. | At most three shell commands and two minutes on the recorded machine/network. | M2 source and release smoke | no | validated |103+| DX-PROTOTYPE-01 | The M1 checkout reaches a retained write quickly. | `deno task dev` to retained write within 60 seconds after cached setup. | integrated M1 prototype | yes | validated |104+| DX-START-01 | Cached development startup feels immediate. | Warm median at most 2 seconds; slowest at most 5 seconds across five samples. | #6 | yes | validated |105+| DX-HMR-01 | UI edits retain state and runtime cardinality. | Median feedback at most 300 ms; one client and one vendor subscription per shared query after five edits. | #5 and #6 | yes | validated |106+| DX-AUTHOR-01 | Product edits avoid framework plumbing. | The integrated task changes only schema, app config, page/island, style, or test files. | integrated prototype | yes | validated |107+| DX-LEAK-01 | Runtime machinery stays outside product UI. | No provider, raw client, worker, transport URL, Workbox config, or browser branch in product UI. | #5, #6, integration | yes | validated |108+| DX-LOCAL-01 | Local work does not await network/auth round trips. | Subscribed UI reflects an offline write in the same event turn or next render; reload retains it. | #7 | yes | validated |109+| DX-DUR-01 | Durable mode never silently becomes ephemeral. | Boot reports durable, unsupported, or explicitly opted-in memory mode. | #4 and #7 | yes | validated |110+| DX-SYNC-01 | Application-data transport has one lofi-owned surface. | App config selects a named adapter; product UI never constructs peers or transports. | #7 and integration | yes | validated |111+| DX-OBS-01 | Diagnostics expose only truthful signals. | Configured state and per-write durability map to public APIs; unavailable transport detail is named. | #7 | yes | validated |112+| DX-ENV-01 | Environment handling is safe by construction. | Real env ignored; allowlisted loader; server values absent from client projection and built output. | #3, #7, final build | yes | validated |113+| DX-ERROR-01 | Failures state capability, impact, and action. | No secret values; common config recovery is one edit plus rerun. | #3, #4, #6 | yes | validated |114+| DX-AUTH-01 | Identity wording matches custody/recovery. | UI identifies the device-local key, blocks the rejected alpha passkey path, and defines any future phrase as an identity bearer secret rather than data backup. | #8 | yes | revised |115+| DX-DEVICE-01 | Device/auth tests use a stable, appropriately isolated origin. | M1 proves a stable HTTPS PWA scope for OPFS; installed-app RP-ID preservation and identity isolation remain required before any replacement passkey ceremony. | #4 and #8 | yes | revised |116+| DX-DEVICE-UX-01 | Device preview is one productized command. | Primary path prints stable URL, capability report, and remediation. | M3 | no | proposed |117+| DX-OFFLINE-01 | Installed production cold-start renders retained data offline. | Airplane-mode launch renders shell and data. | feasibility #4; full M3 | no | proposed |118+| DX-BUILD-01 | Development and production share the Deno command contract. | `deno task build` and `preview`; no undocumented Node command. | #6 | yes | validated |119+| DX-TEST-01 | Local-first tests avoid hand-timed sleeps. | Readiness-based offline/two-client primitives. | #7 and M2 Layer 3 | no | validated |117120 118121 ## Canonical command surface and output contract119122 120-These outputs are implemented and verified from M2 source. The `create` registry invocation remains121-conditional on an explicitly authorized publish and registry-backed smoke; the generated command122-surface itself is covered by the source-backed golden journey.123+These outputs define the generated command surface. Release acceptance also requires a124+registry-backed smoke of the published package.123125 124126 | Command | Success output must contain | Failure output must contain |125127 | ------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------- |126128 | `deno run -A jsr:@nzip/lofi/create <name>` | Created path and exact `cd`/`deno task dev` next steps. | Conflicting input/path, unchanged-files guarantee, and corrective command. |127129 | `deno task dev` | Usable URL plus storage, identity, sync, and PWA states. | Failed subsystem, developer impact, and one remediation. |128130 | `deno task doctor` | Versioned capability/configuration table without values. | Invalid/unsupported item plus remediation; nonzero exit for blockers. |129-| `deno task check` | Checks run and concise pass summary. | First failing check and rerun command. |130131 | `deno task test` | Suites, durations, and retained failure-artifact location. | Failing scenario and artifact location without fixed-sleep advice. |131132 | `deno task build` | Output path, route count, and secret-scan result. | Failed internal tool/adapter and supported fallback action. |132133 | `deno task preview` | Production URL and build identity. | Missing/stale build and exact build command. |@@ -288,6 +289,9 @@ runtime package seams and a second consumer remain M4 work.
288289 289290 ## M2 layered graduation290291 292+> The remainder of this section records the historical M2 integration process. It is retained as293+> decision context, not current release or application guidance.294+291295 M2 integrated into the `m2` branch as three separately reviewed layers. Each layer passed its own292296 checks and independent pull-request review before merge. The combined branch is now being293297 re-reviewed before it may merge into `dev`.@@ -307,9 +311,8 @@ clean-room and review boundary before promotion to `dev`.
307311 | 3 — testing and inspection | #12, #10 | Reusable offline/convergence controls and truthful developer diagnostics without leaking machinery into product UI. |308312 309313 Layer 1 alone did not close #11: checkout-mode evidence was necessary, and Layer 2 subsequently310-passed the source-backed generated-project journey. Registry-backed acceptance remains conditional311-on explicit publication authorization. Remaining abstraction leaks are tracked in312-`docs/m2-abstraction-leaks.md` rather than hidden behind premature package APIs.314+passed the source-backed generated-project journey. Remaining abstraction leaks stayed explicit315+rather than being hidden behind premature package APIs.313316 314317 Layer 2 has two distinct package gates. Before publication, the generated-project journey invokes315318 the real local `./create` entrypoint and uses an explicitly test-only file-URL package override so@@ -325,8 +328,7 @@ it does not call transport pause "browser offline" and does not infer live conne
325328 queue depth, or leader/follower role. Real network control and readiness-first two-client fixtures326329 graduate at `@nzip/lofi/testing`. The convergence fixture uses two isolated OPFS replicas with one327330 identity restored only in memory because the current creator-only permissions do not allow two328-distinct identities to edit the same row. See329-[ADR 0006](decisions/0006-bound-layer3-inspection-and-testing.md).331+distinct identities to edit the same row.330332 331333 Layer 1 keeps M1's `lofi-prototype-<appId>` OPFS namespace so existing device rows remain visible to332334 the reviewed notes-to-tasks lens. Jazz discovers that lens and its snapshots beside `schema.ts` in@@ -0,0 +1,102 @@
1+# Getting started with a generated lofi app2+3+This guide begins after installing [Deno 2.9 or newer](https://docs.deno.com/runtime/).4+5+## Create and run the app6+7+```sh8+deno run -A jsr:@nzip/lofi/create my-app9+cd my-app10+deno task dev11+```12+13+Open the URL printed by Astro. The starter contains a task list, an account panel when sync is14+available, and device/runtime diagnostics.15+16+Verify the local-first baseline before changing anything:17+18+```mermaid19+flowchart LR20+ A["Add a task"] --> B["Reload"]21+ B --> C{"Task remains?"}22+ C -- "yes" --> D["Disconnect network"]23+ D --> E["Add or complete a task"]24+ E --> F{"UI still works?"}25+ C -- "no" --> G["Run doctor"]26+ F -- "no" --> G27+ F -- "yes" --> H["Local-first baseline verified"]28+```29+30+The default app is local-only. It does not require an `.env`, an account ceremony, or a backend.31+32+## Run the project checks33+34+```sh35+deno task doctor36+deno task test37+deno task build38+deno task preview39+```40+41+`doctor` checks the project layout and configuration without printing values. `test` runs the fast,42+deterministic suite. `build` creates the production PWA in `dist/`, and `preview` serves that exact43+output at `http://127.0.0.1:4321/` by default.44+45+## Know which files to change46+47+| File or directory | Purpose |48+| -------------------- | ----------------------------------------------------------- |49+| `src/schema.ts` | Tables and typed fields |50+| `src/permissions.ts` | Read and mutation policies |51+| `src/app.ts` | App name, database name, stable origins, and repository URL |52+| `src/pages/` | Astro pages and shell composition |53+| `src/islands/` | Preact hooks and interactive UI |54+| `src/styles/` | Application styling |55+| `public/` | Manifest, icon, and service-worker assets |56+| `tests/` | Application and local-first browser tests |57+| `src/_lofi/` | Generated framework runtime; do not edit |58+59+Application hooks can import runtime seams from `src/_lofi/`, as the generated `use-tasks.ts` does.60+The boundary means you should not modify the runtime implementation or import Jazz networking and61+storage machinery directly into product components.62+63+## Customize the application identity first64+65+Before accumulating local data, update `src/app.ts`:66+67+- `name` is the human-facing application name.68+- `databaseName` namespaces durable browser storage.69+- `repositoryUrl` controls the starter footer link.70+- `credentialOrigins` lists hostnames you intend to keep stable if you use device credentials.71+72+Also update `public/manifest.webmanifest`, `public/favicon.svg`, the page title, and starter copy.73+74+Changing `databaseName` later points the browser at a different local database. Choose it early so a75+cosmetic rename does not make existing device data appear to disappear.76+77+## Replace the task example78+79+The fastest path is to change one layer at a time while keeping the app runnable:80+81+```mermaid82+flowchart LR83+ A["Edit schema"] --> B["Update permissions"]84+ B --> C["Create domain hook"]85+ C --> D["Build the island UI"]86+ D --> E["Update tests"]87+ E --> F["doctor → test → build"]88+```89+90+Continue with [Data and UI](data-and-ui.md) for the table-to-island pattern and91+[Permissions](permissions.md) before exposing new data.92+93+## Add sync only when you need it94+95+Local-only development is the normal starting point. To add managed Jazz sync and account recovery:96+97+```sh98+deno task jazz:provision99+```100+101+Then follow [Sync and recovery](sync-and-recovery.md). Provisioning makes sync available; each user102+still chooses whether their existing local account should begin syncing.@@ -0,0 +1,66 @@
1+# Permissions2+3+`src/permissions.ts` is an author-owned security boundary. It defines which signed local-first4+identity may read or mutate each table; hiding a button in the UI is not a substitute for policy.5+6+## The starter policy7+8+The generated task app uses creator-owned rows:9+10+```ts11+import { schema as s } from "jazz-tools";12+import { app } from "./schema.ts";13+14+export default s.definePermissions(app, ({ policy, session }) => {15+ policy.tasks.allowInsert.always();16+ policy.tasks.allowRead.where({ $createdBy: session.user_id });17+ policy.tasks.allowUpdate.where({ $createdBy: session.user_id });18+ policy.tasks.allowDelete.where({ $createdBy: session.user_id });19+});20+```21+22+This means:23+24+- Any valid app session may create a task.25+- A task is readable only by the identity that created it.26+- Only that creator may update or delete it.27+- Recovering the same identity restores the same authority; opening a new local account does not.28+29+## Adding a table30+31+Add all four decisions deliberately for each new table:32+33+1. Who may insert?34+2. Who may read a row?35+3. Who may update it?36+4. Who may delete it?37+38+Start with creator ownership unless the product has a concrete collaboration model. Do not make a39+table broadly readable merely to get a UI prototype working.40+41+## Changing an existing policy42+43+Treat a permission change like a data migration:44+45+1. Describe the intended actors and operations in plain language.46+2. Update `src/permissions.ts`.47+3. Run `deno task schema:validate`.48+4. Run the application tests with at least one allowed and one rejected identity case.49+5. Deploy the schema/permission bundle explicitly with `deno task schema:deploy`.50+51+Managed sync configuration is required to publish schema and permission changes. Local-only mode is52+still useful for UI work, but it does not prove a cloud permission policy was deployed correctly.53+54+## Collaboration is not implied55+56+The starter demonstrates private, creator-owned data. It does not generate sharing, invitations,57+roles, revocation, or organization membership. Those features require a deliberate Jazz policy and58+product flow; do not infer them from the presence of sync.59+60+When adding collaboration, test at least:61+62+- the creator's access;63+- an explicitly authorized second identity;64+- an unrelated identity;65+- access after the product's revoke/remove action;66+- offline edits followed by reconnection.@@ -0,0 +1,30 @@
1+# Command reference2+3+Run these from a generated project as `deno task <name>`.4+5+| Task | Purpose |6+| ------------------- | ----------------------------------------------------------------- |7+| `dev` | Validate configuration and run the Astro development server |8+| `doctor` | Print a secret-free project and environment readiness report |9+| `test` | Run the deterministic generated-project test suite |10+| `build` | Build the static PWA, fingerprint it, and scan for leaked secrets |11+| `preview` | Serve an existing production build locally |12+| `jazz:provision` | Create a managed Jazz app and write its configuration to `.env` |13+| `schema:validate` | Validate the Jazz schema and permission declarations |14+| `schema:deploy` | Publish the schema, migrations, and permissions |15+| `migrations:create` | Create a migration under `src/migrations/` |16+| `migrations:push` | Push migrations using the managed Jazz configuration |17+| `deploy:create` | Build and create a static Deno Deploy application |18+| `deploy` | Build and deploy a new production version to the configured app |19+20+## Arguments21+22+```sh23+deno task dev -- --host 0.0.0.024+deno task preview --port 417325+deno task jazz:provision --env .env --force26+deno task deploy:create --org <org> --app <app>27+```28+29+`dev` accepts Astro arguments after `--`. `preview` accepts `--port`. Provisioning accepts an30+alternate environment path and requires `--force` before replacing existing values.@@ -0,0 +1,38 @@
1+# Configuration reference2+3+## Application configuration4+5+`src/app.ts` is author-owned:6+7+| Field | Purpose |8+| ------------------- | -------------------------------------------------------------------- |9+| `name` | Human-facing application name |10+| `databaseName` | Prefix for the durable local database namespace |11+| `schema` | Typed application schema exported by `src/schema.ts` |12+| `storage` | Durable storage policy; generated projects use `"durable"` |13+| `credentialOrigins` | Stable hostnames allowed for optional WebAuthn/device-credential use |14+| `sync.adapter` | Generated sync adapter selection |15+| `repositoryUrl` | Source/home link displayed by the starter |16+17+Choose `databaseName` and stable credential origins before shipping. Changing them later can change18+which local database or WebAuthn relying party the browser opens.19+20+## Environment configuration21+22+| Name | Classification | Behavior |23+| ------------------- | -------------- | ----------------------------------------- |24+| `JAZZ_APP_ID` | Client-visible | Selects the managed Jazz application |25+| `JAZZ_SERVER_URL` | Client-visible | Selects its sync endpoint |26+| `JAZZ_ADMIN_SECRET` | Server-only | Used by schema and administrative tooling |27+| `BACKEND_SECRET` | Server-only | Reserved for server-side integration |28+29+No `.env` selects local-only mode. A complete `JAZZ_APP_ID` and `JAZZ_SERVER_URL` pair makes sync30+available. A partial pair is invalid and blocks affected commands.31+32+Process environment values take precedence over `.env`. Server-only values must never be prefixed as33+public variables, imported into client code, or committed.34+35+## Build-time behavior36+37+The app is static, so public Jazz configuration is projected into the client during development or38+build. Changing the deployment environment requires a new production build.@@ -0,0 +1,38 @@
1+# Generated project layout2+3+```mermaid4+flowchart TB5+ Root["my-app/"]6+ Root --> Config["Project config<br/>deno.json · astro.config.ts · tsconfig.json"]7+ Root --> Env[".env.example"]8+ Root --> Public["public/<br/>favicon · manifest · service worker"]9+ Root --> Src["src/"]10+ Root --> Tests["tests/"]11+12+ Src --> App["app.ts"]13+ Src --> Schema["schema.ts"]14+ Src --> Permissions["permissions.ts"]15+ Src --> Pages["pages/"]16+ Src --> Islands["islands/"]17+ Src --> Styles["styles/"]18+ Src --> Runtime["_lofi/<br/>generated runtime"]19+```20+21+## Author-owned files22+23+- `src/schema.ts` declares persisted tables and their field types.24+- `src/permissions.ts` declares read and mutation policies.25+- `src/app.ts` composes product configuration.26+- `src/pages/`, `src/islands/`, and `src/styles/` contain the product experience.27+- `public/` contains install and static shell assets.28+- `tests/` contains application tests and worked local-first browser examples.29+30+## Generated runtime files31+32+`src/_lofi/` owns durable storage, the Jazz client, account sessions, recovery, lifecycle handling,33+PWA capability gates, diagnostics, and table stores. Do not edit these files during normal product34+work.35+36+The generated domain hook imports selected `_lofi` runtime seams. Follow that pattern when binding a37+new table, but keep vendor setup, storage selection, transports, service-worker logic, and38+capability branching out of product components.@@ -1,8 +1,9 @@
11 # lofi — Local-First Mobile PWA Meta-Framework (Prototype Plan)22 3-> **Status: research input, not the current product contract.** The executable developer-experience4-> contract lives in `docs/devx-contract.md`. M1 spikes are expected to correct or reject claims in5-> this draft as Jazz 2 and target-browser behavior are verified.3+> **Historical prototype plan—do not use as API or template documentation.** Several identities,4+> paths, package exports, and implementation choices below were rejected or replaced. Application5+> developers should use the [current developer documentation](README.md); framework contributors can6+> consult the [developer-experience contract](devx-contract.md) and retained spike evidence.67 78 Working name **`lofi`** (import paths like `@nzip/lofi/core`). Goal: validate that Jazz + Preact89 islands + Astro + Deno can be wrapped into an ergonomic framework where the UI always hydrates from@@ -64,10 +64,10 @@ credential value is recorded here.
6464 | Server-secret build scan | pass | 57 files, including 27 nested build artifacts, scanned against both available server secrets. |6565 | Physical devices | not tested here | Owned by #4 and #8. |6666 67-Screenshots:68-69-- [`output/playwright/jazz2-alpha53-local-offline-multitab.png`](../../../output/playwright/jazz2-alpha53-local-offline-multitab.png)70-- [`output/playwright/jazz2-alpha53-cloud-second-client.png`](../../../output/playwright/jazz2-alpha53-cloud-second-client.png)67+The 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 this70+checkout, so they are not cited as retained evidence here.7171 7272 ## Compatibility findings7373 @@ -1,11 +1,15 @@
11 # OPFS device checklist22 3+> Framework QA checklist. This is retained with the spike evidence and is not part of the normal4+> generated-app onboarding path. Adapt labels and selectors to the current starter before running5+> it.6+37 Issue: [#4](https://github.com/FelineStateMachine/lofi/issues/4)\48 Build: fill from the site's `lofi-build.json`\59 Jazz: 2.0.0-alpha.53610 7-Never record environment values, identity secrets, or note contents that are sensitive. Use unique8-throwaway note labels such as `ios-tab-A-1`.11+Never record environment values, identity secrets, or task contents that are sensitive. Use unique12+throwaway task labels such as `ios-tab-A-1`.913 1014 ## Record before testing1115 @@ -30,17 +34,18 @@ a memory fallback.
3034 3135 ## Durability3236 33-Use a new throwaway label for each row and confirm it appears in both islands.37+Use a new throwaway label for each row. Confirm it appears in the task list and that the status line38+reports local durability.3439 3540 | Test | Action | Required result | Result |3641 | ------------------- | ------------------------------------------------------------ | --------------------------------------- | ------ |37-| Reload | Add note, reload page | Note returns | |38-| Browser termination | Add note, terminate browser from app switcher, reopen URL | Note returns | |39-| App termination | Installed surface: add note, terminate installed app, reopen | Note returns | |40-| Device restart | Add note, restart device, reopen same surface | Note returns | |41-| Background | Add note, background 5+ minutes, foreground | Note remains; app becomes reactive | |42-| Offline foreground | Load once, airplane mode, reopen installed surface, add note | Shell and local write work | |43-| Reconnect | Disable airplane mode | Local note remains; no false sync claim | |42+| Reload | Add task, reload page | Task returns | |43+| Browser termination | Add task, terminate browser from app switcher, reopen URL | Task returns | |44+| App termination | Installed surface: add task, terminate installed app, reopen | Task returns | |45+| Device restart | Add task, restart device, reopen same surface | Task returns | |46+| Background | Add task, background 5+ minutes, foreground | Task remains; app becomes reactive | |47+| Offline foreground | Load once, airplane mode, reopen installed surface, add task | Shell and local write work | |48+| Reconnect | Disable airplane mode | Local task remains; no false sync claim | |4449 4550 ## Multi-tab / multi-window4651 @@ -48,7 +53,7 @@ Use a new throwaway label for each row and confirm it appears in both islands.
4853 2. Confirm both show `persistent-driver-open`.4954 3. Add `A-<timestamp>` in A; confirm B updates without reload.5055 4. Add `B-<timestamp>` in B; confirm A updates without reload.51-5. Submit once from both surfaces as closely together as possible; confirm both distinct notes56+5. Submit once from both surfaces as closely together as possible; confirm both distinct tasks5257 appear in both surfaces.5358 6. Close A; write in B; reopen A and confirm the write.5459 7. Confirm every live page shows one active client and one underlying subscription locally. Counts@@ -0,0 +1,73 @@
1+# Sync and recovery2+3+Sync is optional infrastructure layered onto an account that already works locally. Configuring a4+Jazz app makes sync available; it does not automatically upload every user's local data. Each user5+elects to back up and sync from the generated `AccountGate` UI.6+7+## Provision a managed Jazz app8+9+From an existing generated project:10+11+```sh12+deno task jazz:provision13+```14+15+Or provision during project creation:16+17+```sh18+deno run -A jsr:@nzip/lofi/create --sync my-app19+```20+21+Provisioning writes four names to the git-ignored `.env`:22+23+| Name | Exposure | Purpose |24+| ------------------- | -------------- | --------------------------------------- |25+| `JAZZ_APP_ID` | Client-visible | Identifies the managed Jazz application |26+| `JAZZ_SERVER_URL` | Client-visible | Selects the managed sync endpoint |27+| `JAZZ_ADMIN_SECRET` | Server-only | Authorizes schema/permission operations |28+| `BACKEND_SECRET` | Server-only | Reserved server-side credential |29+30+Claim the generated Jazz application within the window printed by the command. Keep `.env` private;31+the build projects only the complete public pair and scans the client output for secret values.32+33+Run `deno task doctor` after provisioning. A partial public pair is invalid: set both public names34+or remove both to return to local-only mode.35+36+## The user's account journey37+38+```mermaid39+stateDiagram-v240+ [*] --> LocalOnly: First boot41+ state "Local-only<br/>Private on-device account<br/>Durable local data" as LocalOnly42+ LocalOnly --> Syncing: User opts in and saves phrase43+ state "Syncing<br/>Same account identity<br/>Local and global durability" as Syncing44+ Syncing --> LocalOnly: Stop syncing45+ Syncing --> Recovered: Enter phrase on another device46+ state "Recovered<br/>Same identity restored<br/>Synced data downloads" as Recovered47+ Recovered --> Syncing48+```49+50+Data created before opt-in carries forward because enabling sync does not replace the account51+secret.52+53+## Recovery guarantees and limits54+55+- The recovery phrase is the account authority. Anyone with all the words can act as that account.56+- lofi does not retain material that can reconstruct the account for the user.57+- Losing both the device and every copy of the phrase loses the account.58+- Recovering a phrase can retrieve data that was synced. It cannot reconstruct writes that existed59+ only in storage on a lost device.60+- Restoring replaces the current device account, so the generated UI confirms before abandoning an61+ unsynced local identity.62+63+Read [Identity and recovery model](auth-identity.md) for the detailed state machine and custody64+model.65+66+## Before shipping sync67+68+- Customize the account copy without weakening the recovery warnings.69+- Confirm the production build receives the intended public configuration.70+- Run `deno task build` and verify the secret scan passes.71+- Test recovery using throwaway data and a second browser profile or device.72+- Run the two-client convergence example described in [Testing](testing.md).73+- Decide how users will store their phrase safely; there is no server-assisted reset flow.@@ -0,0 +1,107 @@
1+# Testing a lofi app2+3+The generated template separates fast deterministic tests from opt-in browser scenarios that need a4+running app, Chromium, and—for convergence—managed sync.5+6+```mermaid7+flowchart TB8+ Change["Application change"] --> Fast["deno task test<br/>deterministic suite"]9+ Fast --> Build["build + preview<br/>production shell"]10+ Build --> Manual["reload and offline checks"]11+ Build --> Browser["two-client convergence<br/>opt-in Playwright"]12+ Manual --> Device["physical iOS and Android"]13+ Browser --> Device14+ Device --> Ship["Ready to ship"]15+```16+17+## Run the default suite18+19+```sh20+deno task test21+```22+23+The default suite covers the generated runtime contracts and application tests without launching a24+browser. Keep domain logic and permission-shape checks in this fast path when possible.25+26+## Test the production build manually27+28+```sh29+deno task build30+deno task preview31+```32+33+Then verify:34+35+1. Add data and reload.36+2. Confirm the status reports local durability.37+3. Disable the network and continue reading and writing.38+4. Reload while offline to exercise the production service worker.39+5. Restore the network and confirm the local data remains.40+41+The service worker is intentionally disabled during `deno task dev`; use a production build when42+testing offline shell startup.43+44+## Run the two-client convergence example45+46+First configure managed sync and start the app:47+48+```sh49+deno task jazz:provision50+deno task dev51+```52+53+In another terminal, point the included browser example at the printed URL:54+55+```sh56+LOFI_E2E_BASE_URL=http://127.0.0.1:4321/ \57+ deno test -A tests/convergence_e2e_test.ts58+```59+60+```mermaid61+sequenceDiagram62+ participant A as Client A63+ participant B as Client B64+ participant S as Managed sync65+66+ A->>A: Go offline and edit67+ B->>B: Go offline and edit68+ A->>S: Reconnect and upload69+ B->>S: Reconnect and upload70+ S-->>A: Both edits71+ S-->>B: Both edits72+ A->>A: Assert convergence73+ B->>B: Assert convergence74+```75+76+Both browser contexts use one test identity. Without `LOFI_E2E_BASE_URL`, the example skips so the77+default suite remains fast.78+79+If Chromium is missing:80+81+```sh82+deno run -A npm:playwright@1.61.1 install chromium83+```84+85+## Adapt the example to your UI86+87+Update these four pieces in `tests/convergence_e2e_test.ts`:88+89+- `ready` — a DOM condition proving the local store has hydrated;90+- `apply` — the user action that makes one local mutation;91+- `locallyApplied` — proof that the offline client rendered its own mutation;92+- `converged` — proof that both clients eventually render all expected results.93+94+Use stable accessible roles, labels, and application-owned `data-*` attributes. Avoid arbitrary95+sleeps; readiness helpers retry observable conditions until their timeout.96+97+## Failure artifacts98+99+Browser fixtures can save artifacts under `test-results/`. Snapshot callbacks should contain counts,100+booleans, state names, or sanitized identifiers—not task text, environment values, recovery phrases,101+or other user data.102+103+## Physical-device checks104+105+Browser automation does not prove installed-PWA storage and lifecycle behavior on iOS or Android.106+Before shipping, use a stable HTTPS origin and exercise installation, termination, device restart,107+offline cold start, foreground recovery, and account recovery on every supported mobile surface.@@ -0,0 +1,95 @@
1+# Troubleshooting2+3+Start with:4+5+```sh6+deno task doctor7+```8+9+The report names the blocked capability or configuration and gives a remediation without printing10+environment values.11+12+## The app says configuration is incomplete13+14+Managed sync requires both public values:15+16+```text17+JAZZ_APP_ID18+JAZZ_SERVER_URL19+```20+21+Set both, or remove both to run local-only. Do not move `JAZZ_ADMIN_SECRET` or `BACKEND_SECRET` into22+client-visible variables.23+24+If provisioning should replace an existing `.env`, review and back up the intended configuration,25+then use the command's explicit `--force` option:26+27+```sh28+deno task jazz:provision --force29+```30+31+## Durable storage is blocked32+33+lofi requires a secure context, OPFS, SharedWorker, Web Locks, and MessageChannel. It stops instead34+of silently switching to memory-only data.35+36+- Use a supported browser: Android Chrome 148+ or iOS Safari 16.4+.37+- Use `localhost` during development or HTTPS on another device.38+- Avoid embedded/private browser surfaces that disable required storage APIs.39+- Treat clearing site data as destructive unless the account has synced and the recovery phrase is40+ safe.41+42+## A task disappeared after changing app configuration43+44+Check whether `databaseName` or the managed `JAZZ_APP_ID` changed. Both participate in the durable45+storage namespace. Restoring the old values reopens the old namespace; copying rows between46+namespaces is not automatic.47+48+## Preview says the build is missing or stale49+50+Run:51+52+```sh53+deno task build54+deno task preview55+```56+57+Do not hand-create `dist/lofi-build.json`; it must describe the output produced by the build.58+59+## The production build reports a secret leak60+61+Remove the named server-only value from source, public files, logs, fixtures, and generated client62+configuration. Rotate a real credential if it entered source control or an artifact, then rebuild.63+64+## Browser tests skip or cannot launch65+66+Set the base URL when running the opt-in example:67+68+```sh69+LOFI_E2E_BASE_URL=http://127.0.0.1:4321/ \70+ deno test -A tests/convergence_e2e_test.ts71+```72+73+Install the pinned Chromium runtime if needed:74+75+```sh76+deno run -A npm:playwright@1.61.1 install chromium77+```78+79+## Local writes work but another device does not update80+81+Check these states separately:82+83+1. The deployment has a complete public Jazz pair.84+2. The user explicitly elected to sync in `AccountGate`.85+3. The latest write reached global durability rather than only local durability.86+4. Both devices restored the same account identity.87+5. The deployed schema and permissions allow the operation.88+89+“Sync configured” is not proof that a specific user opted in or that a specific write reached the90+server.91+92+## Recovery does not restore a recent item93+94+The phrase restores account authority, not unsynced device storage. Data returns only if it reached95+managed sync before the original device was lost or cleared.@@ -46,6 +46,8 @@ function generatedReadme(name: string): string {
4646 4747 This local-first PWA was created with \`@nzip/lofi\`.4848 49+[Framework documentation](https://github.com/FelineStateMachine/lofi/tree/main/docs)50+4951 \`\`\`sh5052 deno task dev5153 \`\`\`@@ -64,6 +66,10 @@ to sync never changes its identity. See the framework side in \`src/_lofi/sessio
6466 Public tasks: \`dev\`, \`doctor\`, \`test\`, \`build\`, and \`preview\`. Sync/backup and schema tasks:6567 \`jazz:provision\`, \`schema:validate\`, \`schema:deploy\`, \`migrations:create\`, and \`migrations:push\`.6668 69+Start with the framework's70+[generated-app guide](https://github.com/FelineStateMachine/lofi/blob/main/docs/getting-started.md)71+when replacing the task example with your own schema, permissions, hook, and UI.72+6773 ## Hosting6874 6975 \`deno task build\` emits a static PWA in \`dist/\`. The deploy tasks host it on Deno Deploy as a@@ -6,7 +6,7 @@
66 "public/favicon.svg": "23efdb888dae688715faafe3ef94f7b5ad1976afcc715c0a742218b54732c6ad",77 "public/manifest.webmanifest": "7d492285fd982e6e68fa04f12d78cd4f0bef4343f0408e314f1582029f3ea407",88 "public/sw.js": "6fa9f4c4b5ef7baabd48256a91520c766a4ffaf75c261686384d6d13e663d0c2",9- "README.md": "e4434ba89b916d0b832a9044497e1538f35873dbc0b1c49001f1a3d0f54f4e78",9+ "README.md": "51d08f08312c9a555368e1e57a265dad7cc540016d8d4031b7429e6d577e5471",1010 "src/_lofi/auth_test.ts": "771789f0bc48f27c95340e7d8dac452cf87a63e536736a00ac006433aa16e83b",1111 "src/_lofi/auth.ts": "19c0cfb08f0c00a05421bdcfcbd01ad099e52997793cbf0e89de0e25d385fc4f",1212 "src/_lofi/boot.ts": "0f813fbf75d0ba420dcf893e2762ec84be75869b70ef4f197a367c691773474b",@@ -36,7 +36,7 @@
3636 "src/_lofi/ui-mutation_test.ts": "16153da95e080570af254eb0c56433f2db8abacec542984f59f6afd4ebf1c501",3737 "src/_lofi/ui-mutation.ts": "3273053a796160556045d06d672f4c79d9597e1f822928f1ee71a7d31865dc32",3838 "src/_lofi/use-device-capabilities.ts": "f7c7b2321b400d8076a5347e0f1ff2924623535979d48a2d94a69f825b93841e",39- "src/app.ts": "7e65c5308f645164357b02944d1e265f2be942686a634c552add14cfb8fd384f",39+ "src/app.ts": "4b4a5c77b92d59e38649e87c28db5890520c31290de15a438b2b67ff8608553f",4040 "src/env.d.ts": "b44daed05ec5cdfacfd8d8acf7866974b5f6b8db923ab53bd244419093c719da",4141 "src/islands/AccountGate.tsx": "7355d496180ee45a6144f10b96d1ec39e2d4427ffeab6e574c9b78a601480138",4242 "src/islands/TaskList.tsx": "a3d054078819ea8b5609a014e94878bf1b21b2908c340237ed029e80e58f2349",