clone

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

feat/184-task-notice-migration
lofi / docs / devx-contract.md
devx-contract.md 29.7K rendered
1# lofi developer-experience contract23Status: **framework contributor contract with retained M1/M2 history**\4Scope: **product promises, M1 feasibility, and M2 DevX graduation**\5Last reviewed: **2026-07-17**67> 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.1011This is the product contract for lofi. Implementation choices are provisional until they satisfy12these promises. Each M1 spike must cite the contract IDs it tested, retain repeatable evidence, and13record a contract delta even when the result does not change the contract.1415The older [prototype seed](seed.md) is research input, not an authoritative description of Jazz or16browser behavior.1718## Status and merge rules1920- **proposed** — a falsifiable promise awaiting evidence.21- **validated** — demonstrated by retained automated or device evidence.22- **revised** — changed because evidence disproved or narrowed the original promise.23- **deferred** — valuable but intentionally owned by a later milestone.24- **rejected** — disproved or incompatible with the product direction.2526The promises marked **M1 gate: yes** were validated, revised, or rejected before M1 merged. A27post-M1 promise may remain proposed only when it has a named milestone and does not affect the28honesty of the graduated reference application.2930## Two distinct journeys3132### North-star generated-project journey3334This is the current generated-project product journey:3536```sh37deno run -A jsr:@nzip/lofi/create my-app38cd my-app39deno task dev40```4142The JSR surface is one package, `@nzip/lofi`, with one version and publish operation. Its supported43public surface includes the configured browser runtime, Preact integration, Astro integration,44generator, command subpaths, `./access`, and `./testing`. Framework implementations remain45package-owned.4647From there the developer makes a retained local write, reloads it, works offline, runs checks,48builds, previews, and opens the same stable secure origin on a physical device.4950The pull-request release gate runs that journey against a generated checkout, including managed sync51opt-in, local-row migration, recoverable-passkey restore in a fresh browser profile, stable52principal equality, synced-row download, recovery-phrase fallback, and return to an offline managed53replica. The same `deno task check` invocation includes Jazz permission-harness coverage for54private, direct-shared, and fixed-role group policies. Browser failure evidence remains sanitized55and is retained only when the gate fails. Virtual-authenticator results are not physical-device or56cross-provider portability claims.5758### M1 graduation journey5960M1 validates the stack and contract from a repository checkout, without pretending that the M261generator exists:6263```sh64git clone https://github.com/FelineStateMachine/lofi.git65cd lofi66deno task dev67```6869The graduated M1 application, now retained as `apps/reference`, must support a local write, reload,70offline cold-start and edit, check, test, build, and preview. It does not claim observable transport71reconnection or convergence because the pinned Jazz API exposes neither signal; that remains a later72test contract. Git is a contributor prerequisite for this repository journey; Deno remains the only73required global runtime for a generated application.7475## End-to-end journey contract7677| Step | Developer action | Required result | Representative failure and recovery |78| ------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |79| 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. |80| 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. |81| 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. |82| 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. |83| 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. |84| 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. |85| 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. |86| 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. |87| 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. |8889## Measurement protocol9091- Record Deno, package, OS, browser, device, commit SHA, and whether caches were warm.92- Time uncached creation separately from cached startup; dependency download is never hidden from93 the uncached result.94- For startup and HMR, retain five samples and report median plus slowest sample. Startup begins95 when the command is invoked and ends when the usable URL is printed. HMR begins when a watched96 file is saved and ends when the updated UI is observable.97- A **retained local write** must survive a full page reload or explicit client recreation.98- A **subscription leak** means the count after five edit/reload cycles exceeds the initial steady99 count for the same mounted consumers.100- Common configuration failures must be recoverable with one described edit and one rerun; record101 whether recovery took more than 60 seconds.102- Device results retain the manual checklist plus logs or screenshots; “worked once” is not103 evidence.104105## Measurable promises106107| ID | Promise | v0 budget or condition | Evidence owner | M1 gate | Status |108| --------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------- | --------- |109| 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 |110| 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 |111| 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 |112| 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 |113| 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 |114| 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 |115| 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 |116| 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 |117| 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 |118| DX-DUR-01 | Durable mode never silently becomes ephemeral. | Boot reports durable, unsupported, or explicitly opted-in memory mode. | #4 and #7 | yes | validated |119| 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 |120| 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 |121| 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 |122| 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 |123| DX-AUTH-01 | Identity wording matches custody/recovery. | UI distinguishes the local account, recoverable passkey, local phrase guard, and phrase bearer secret; restore confirms replacement and states provider/RP-ID limits. | #8 history and generated golden | yes | validated |124| DX-DEVICE-01 | Device/auth tests use a stable, appropriately isolated origin. | RP-ID follows the exact stable hostname; credential retrieval succeeds there and a sibling RP is rejected without storing secret material. | #4, #8, M3, golden gate | yes | validated |125| DX-DEVICE-UX-01 | Device preview is one productized command. | Primary path prints stable URL, capability report, and remediation. | M3 | no | validated |126| DX-OFFLINE-01 | Installed production cold-start renders retained data offline. | Installed PWA retains a local write across reload and airplane-mode cold start. | feasibility #4 and M3 | no | validated |127| DX-BUILD-01 | Development and production share the Deno command contract. | `deno task build` and `preview`; no undocumented Node command. | #6 | yes | validated |128| DX-TEST-01 | Local-first tests avoid hand-timed sleeps. | Readiness-based offline/two-client primitives. | #7 and M2 Layer 3 | no | validated |129130## Canonical command surface and output contract131132These outputs define the generated command surface. Release acceptance also requires a133registry-backed smoke of the published package.134135| Command | Success output must contain | Failure output must contain |136| ------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------- |137| `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. |138| `deno task dev` | Usable URL plus storage, identity, sync, and PWA states. | Failed subsystem, developer impact, and one remediation. |139| `deno task doctor` | Versioned capability/configuration table without values. | Invalid/unsupported item plus remediation; nonzero exit for blockers. |140| `deno task test` | Suites, durations, and retained failure-artifact location. | Failing scenario and artifact location without fixed-sleep advice. |141| `deno task build` | Output path, route count, and secret-scan result. | Failed internal tool/adapter and supported fallback action. |142| `deno task preview` | Production URL and build identity. | Missing/stale build and exact build command. |143144Example successful development output:145146```text147lofi dev148Local: http://localhost:4321149Storage: OPFS durable150Identity: local-first; passkey backup not configured151Sync: configured; last write confirmed global; live connection detail unavailable152PWA: development service worker disabled153```154155Example configuration failure:156157```text158lofi environment mode: invalid159error: Cloud sync cannot start because configuration is incomplete; set JAZZ_SERVER_URL or remove160the partial JAZZ_APP_ID/JAZZ_SERVER_URL pair to run local-only.161```162163## Generated-project layout164165```text166my-app/167├── deno.json168├── .env.example169├── public/170├── src/171│ ├── schema.ts172│ ├── permissions.ts173│ ├── app.ts174│ ├── pages/175│ ├── layouts/176│ ├── islands/177│ └── styles/178└── tests/179```180181The exact layout may change only when verified toolchain behavior requires it or the change removes182an author-facing concept. Ignored `.lofi/` tooling files and `dist/sw.js` are regenerated from the183pinned package; they are not author-editing surfaces.184185## Author boundary186187| Location | Default allowance | M1 qualification |188| ------------------------ | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |189| Product UI/pages/islands | lofi components/hooks and application types only. | Raw Jazz, worker, transport, Workbox, and capability branching are forbidden. |190| `schema.ts` | Application schema declarations. | Declared through `@nzip/lofi/schema`, the curated one-to-one re-export of the pinned Jazz 2 DSL; see `docs/decisions/schema-facade-alpha53.md`. |191| `permissions.ts` | Application access policy next to the schema. | Declared through the same `@nzip/lofi/schema` surface; Jazz tooling watches this location. |192| `app.ts` | Named lofi configuration and composition. | Raw vendor setup is allowed only inside spike controls, not the graduated prototype. |193| Package runtime | Vendor clients, workers, storage, sync, auth, and PWA wiring. | May change across package pins without changing product UI. |194| Escape-hatch module | Explicitly isolated unsupported vendor access. | Never generated by default; package upgrades provide no compatibility promise for vendor internals. |195196## Safe defaults197198- **Schema:** expose the pinned Jazz 2 DSL through the `@nzip/lofi/schema` re-export — identical199 names and semantics, curated to the tested surface; never invent a second schema language.200- **Identity:** create or restore the verified local-first identity without a server round trip;201 passkey backup is never mislabeled as conventional login.202- **Sync:** no config means clearly reported local-only mode if supported; a complete public pair203 enables the named managed adapter.204- **Storage:** request the verified durable browser driver; unsupported capability stops boot or205 requires an explicit development-only ephemeral opt-in.206- **PWA:** prerendered shell and offline production behavior are the target, while service-worker207 ownership remains outside Jazz storage/sync.208- **Testing:** unit and browser controls use readiness signals; physical-device gaps stay explicit.209- **Security:** generated examples contain names only; server credentials never enter client210 projection, logs, QR codes, build output, or browser assets.211212## Environment contract213214The four names are lofi's configuration API. #7 confirmed that the complete public pair can be215mapped at the Deno/Vite edge without projecting the server-only pair. The vendor's216`VITE_JAZZ_APP_ID` and `VITE_JAZZ_SERVER_URL` names remain internal implementation details.217218| Name | Provisional classification | Contract |219| ------------------- | -------------------------- | ----------------------------------------------------------------------------- |220| `JAZZ_APP_ID` | client-visible identifier | Enters client config only as part of a validated complete public pair. |221| `JAZZ_SERVER_URL` | client-visible endpoint | Enters the named adapter; raw product-UI use is forbidden. |222| `JAZZ_ADMIN_SECRET` | server-only secret | Never projected, logged, generated with a value, or built into client assets. |223| `BACKEND_SECRET` | server-only secret | Never projected, logged, generated with a value, or built into client assets. |224225The allowlisted loader reads an optional ignored `.env`; an explicitly set process value wins.226Missing public configuration selects local-only mode if #7 verifies support. Partial or invalid227cloud configuration is `invalid`, stops the relevant command, and cannot be projected into client228config. Server-only values without client configuration remain isolated and produce a warning.229230Root-repository ignore and leak tests are supplemented by generated-project fixture inspection,231production build scanning, and retained-artifact scans. Publication still requires a fresh registry232smoke and release-artifact review; source acceptance does not imply that a package exists on JSR.233234## Escape-hatch policy235236- An escape hatch exists only for a capability blocked by the public contract and demonstrated by a237 retained use case.238- It is isolated from ordinary product files and named as unsupported.239- It may expose pinned vendor APIs but receives no compatibility guarantee.240- Promotion requires two independent consumers or a critical use case, automated coverage, and a241 decision record describing the new maintenance obligation.242- Node build fallback, raw Jazz access, memory storage, and custom service-worker ownership are not243 public defaults.244245## Non-goals through M1246247- Publishing packages or the generator.248- Designing a provider-neutral data abstraction.249- Reimplementing Jazz schema, auth, permissions, storage, or sync.250- Building the full inspector, test SDK, invitation UX, push support, or production update UX.251- Claiming support for untested browsers/devices or hiding unavailable durability signals.252- Optimizing bundle size before the selected integration works reliably.253254## M1 acceptance checklist255256- [x] Optional `.env` and process precedence select the expected mode without printing values.257- [x] Exact Jazz alpha, Deno, Astro, Preact, browser, and executed OS versions are retained;258 physical device versions are explicitly deferred to M3 by the M1 approval.259- [x] Vendor control proves local write, subscription, reload retention, and optional cloud sync.260- [x] Observability inventory maps each promised diagnostic to evidence or removes it.261- [x] Preact decision passes mount/update/unmount/recreate and five-cycle HMR checks.262- [x] Two Astro islands share exactly one client and update each other.263- [x] `deno task dev`, `check`, `test`, `build`, and `preview` work from the checkout.264- [x] Startup/HMR measurements follow the declared protocol.265- [x] OPFS evidence distinguishes durable, unsupported, and memory fallback, retains the complete266 physical checklist for M3, and records the product owner's M1 HTTPS-artifact approval.267- [x] Identity ceremony testing stopped at the security pre-gate; the unsafe alpha passkey design is268 rejected rather than exercised or implied to work.269- [x] Auth evidence rejects the unsafe ceremony, makes a stable isolated RP ID a replacement270 precondition, states that a recovery phrase is a bearer secret, and defines honest first-run,271 backup, and unrecoverable-loss wording.272- [x] Integrated prototype works offline and reloads retained local data without a false transport273 reconnection claim.274- [x] Final build passes server-secret scanning and contains no development-only inspector.275- [x] Every M1-gated promise is validated, revised, or rejected with evidence.276- [x] Final contract and decisions identify all remaining post-M1 work without implying completion.277278## M1 falsification map279280| Issue | Primary contract IDs |281| -------------------- | --------------------------------------------------------- |282| #7 Jazz 2 baseline | DX-LOCAL-01, DX-DUR-01, DX-SYNC-01, DX-OBS-01, DX-TEST-01 |283| #5 Preact strategy | DX-HMR-01, DX-AUTHOR-01, DX-LEAK-01 |284| #6 Astro and Deno | DX-CMD-01, DX-START-01, DX-HMR-01, DX-BUILD-01 |285| #4 OPFS devices | DX-DUR-01, DX-ERROR-01, DX-OFFLINE-01 |286| #8 identity/recovery | DX-AUTH-01, DX-DEVICE-01 |287288## Graduation rule289290M1 graduated exactly one integrated application, now named `apps/reference`, containing the approved291Jazz version, Preact strategy, Astro/Deno path, durability policy, and identity model. Rejected292runtime alternatives are removed from the final tree; their evidence remains in decisions and293merge-commit history.294295M2 graduated the single-package command surface, generator, development inspector, and296readiness-first browser-testing helpers. M3 graduated representative mobile PWA validation, and the297current generated-app release gate covers install/update output, package-owned runtime seams,298recoverable account replacement, and collaboration policy checks. A second independent consumer299remains later adoption work.300301## M2 layered graduation302303> The remainder of this section records the historical M2 integration process. It is retained as304> decision context, not current release or application guidance.305306M2 integrated into the `m2` branch as three separately reviewed layers. Each layer passed its own307checks and independent pull-request review before merge. The combined branch is now being308re-reviewed before it may merge into `dev`.309310Layer verification runs outside the product and framework surface. An independent agent checks the311exact pushed head in a detached Git worktree, runs `deno task check` plus the layer's applicable312golden journey, and confirms that the retained report names the tested commit. The pull request is a313communication channel for the monitoring reviewer, not a reason to impose a permanent per-push CI314workflow on the framework or generated applications. A layer may merge only when both the clean-room315result and independent review accept that exact head. The final `m2` combination repeats the same316clean-room and review boundary before promotion to `dev`.317318| Layer | Issues | Contract result |319| ---------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |320| 1 — reference and checkout journey | #9; tracks #11 | Honest checklist CRUD, explicit author/generated boundary, and reusable checkout-mode golden-path evidence. |321| 2 — public command path | #13, #14; source gate for #11 | Non-interactive creation plus actionable `dev`/`doctor`; the same journey runs against generated output. |322| 3 — testing and inspection | #12, #10 | Reusable offline/convergence controls and truthful developer diagnostics without leaking machinery into product UI. |323324Layer 1 alone did not close #11: checkout-mode evidence was necessary, and Layer 2 subsequently325passed the source-backed generated-project journey. Remaining abstraction leaks stayed explicit326rather than being hidden behind premature package APIs.327328Layer 2 has two distinct package gates. Before publication, the generated-project journey invokes329the real local `./create` entrypoint and uses an explicitly test-only file-URL package override so330all generated commands execute from the exact source under review. The default generated output331still pins `jsr:@nzip/lofi` to the manifest version. This source gate does not claim that the332registry command exists: after source acceptance and explicit publication authorization, a333fresh-directory smoke must invoke `deno run -A jsr:@nzip/lofi/create <name>` before #13 and #11 can334close.335336Layer 3 keeps inspection and testing semantics distinct. The generated development inspector may337pause a configured Jazz transport, restart the client, and confirmation-gate an OPFS replica clear;338it does not call transport pause "browser offline" and does not infer live connectivity, vendor339queue depth, or leader/follower role. Real network control and readiness-first two-client fixtures340graduate at `@nzip/lofi/testing`. The convergence fixture uses two isolated OPFS replicas with one341identity restored only in memory because the current creator-only permissions do not allow two342distinct identities to edit the same row.343344Layer 1 keeps M1's `lofi-prototype-<appId>` OPFS namespace so existing device rows remain visible to345the reviewed notes-to-tasks lens. Jazz discovers that lens and its snapshots beside `schema.ts` in346`apps/reference/src/migrations`. `deno task check:migrations` couples the current schema hash,347incoming migration edge, and snapshot; `deno task schema:deploy` is the explicit cloud publication348step for the schema, migration, and permissions bundle.