# Offline service notes

A working local application for a technician's text notes and service checklist. The browser saves a note and its pending operation together in IndexedDB. A Node/SQLite server accepts assigned visits, checks record versions, records idempotent responses, and returns conflicting versions for review.

The included visits and accounts are synthetic. This package is a localhost reference implementation, not a hosted service or production identity system.

## Start

Use Node.js 24 or newer. No npm packages are required. Run from this directory:

```sh
node server.mjs
```

Open `http://localhost:8787`. The first start creates `data/notes.sqlite` and three sample visits. Stop with Ctrl+C. The port defaults to 8787; `PORT` selects another port, and `NOTES_DB` selects a database file. For example, in a POSIX shell:

```sh
PORT=8788 NOTES_DB=/tmp/service-notes-demo.sqlite node server.mjs
```

On PowerShell, set `$env:PORT='8788'` and `$env:NOTES_DB='C:\Temp\service-notes-demo.sqlite'` before running `node server.mjs`.

The server binds only to 127.0.0.1 and checks Host and Origin. Keep it local. The `public/` directory alone is not a functioning application because it also needs the API.

## Use the application

1. Leave the demo account on Alex, then refresh assigned visits.
2. Open AHU-014, edit its notes or checklist, and select **Save on this device**. The record now says it is pending sync.
3. Select **Sync pending changes**. A confirmation includes the server version. A failed connection leaves the saved draft available.
4. Use **Export local records** to download saved notes, pending operations, and conflict history as JSON. Unsaved form text is not included.

Alex can access all three visits. Riley can access AHU-014 and PUMP-008. Dispatcher can read visits and manage assignments through the API, but cannot edit a technician's note through the UI. The account selector uses public demonstration tokens defined in `server.mjs`. It is deliberately not a sign-in system.

Demo sessions expire 24 hours after a database is created. To run another fresh demonstration, stop the server and start it with a new `NOTES_DB` path. Preserve any previous database or exports you need. There is no packaged runtime database or private credential.

## Try a conflict

1. Open the application and its **second workspace** link in two tabs. Both tabs should refresh the same visit before either changes it.
2. Save different notes in each workspace.
3. Sync the first workspace, then the second. The second shows its local draft beside the server copy.
4. Choose **Use server version**, or review and edit the local draft before selecting **Keep my draft**. The latter submits a new operation against the displayed server version.

The workspace query parameter selects a separate IndexedDB name, so the two tabs represent independent local stores. It does not simulate two different hardware devices. User accounts also use separate local stores.

## Verification

```sh
node --test test-server.mjs
node verify.mjs
```

The first command runs 15 HTTP/SQLite checks. `verify.mjs` reruns them, writes the TAP log and `verification.json`, and refreshes the offline HTML report. It also executes the synthetic worker, process recovery, snapshot CLI and deployment model through complete.mjs. It does not automate a browser. The browser result in that report is a separately observed run, with its method and limits in `results/browser-verification.json`.

Open `http://localhost:8787/tests.html`, select **Run checks**, and download the result to rerun the 16 browser checks. They use real IndexedDB transactions in temporary, isolated stores and injected protocol responses. The separate API suite uses real HTTP and SQLite. The browser tests cover rollback, persistence after database reopen, replay, newer local edits, conflicts, rejected access, expired access, and two local device stores. They do not alter the application's saved visits.

The provided Chrome preview passed all 16 IndexedDB checks. Its live UI also loaded visits, saved a note, and synchronized it as server version 2. A second browser workflow used two independent workspaces, produced an explicit conflict after the first reached version 3, and selected the server copy while retaining the local draft in history. The screenshot is included in `figures/service-notes.png`. This is a verification fixture, not a customer deployment or usage claim.

## Offline shell check

A service worker caches the application shell. HTTPS or localhost is required. After an initial load at `http://localhost:8787`, wait for service-worker activation, reload once, then stop the server and reload the page. Previously cached visits should remain available for editing and saving; syncing should report the unavailable connection and preserve pending operations. Restart the server and sync.

The supplied QA environment used HTTP on `terminal.local`, so this secure-context browser scenario was completed with a synthetic lifecycle run of the actual worker source. `node test-sw.mjs` verifies install failure, offline navigation, HTTP 503 fallback, asset loading, cache upgrades, scope isolation and eviction. The recorded Chrome IndexedDB tests separately verify actual storage and protocol behavior. The simulation does not claim browser registration. Do not treat browser data as a permanent backup: storage can be cleared or evicted.

## Record and retry behavior

- A browser transaction saves the note and outgoing operation together.
- Unsent edits coalesce. Once an operation has been attempted, its ID and payload remain fixed for retry.
- The server commits the record update and replay response together. Reusing an ID with different content is rejected.
- A stale base version returns the current server record without applying the write.
- Conflict resolution retains the previous local draft in export history.
- An old acknowledgement cannot overwrite a newer local draft.
- Current assignment and session checks apply to writes, including replays.

The API limits notes to 12,000 characters and request bodies to 64 KiB. It stores text and three Boolean checklist fields. It has no photo attachments, multi-tenant administration, production authentication, encrypted browser storage, automatic background synchronization, or retention scheduler. It uses SQLite, not PostgreSQL. No production load, mobile browser, or multi-device hardware testing is claimed.

## Files

- `server.mjs`: localhost HTTP API, assignment checks, transactions, response replay.
- `public/store.mjs`: IndexedDB transaction and conflict history logic.
- `public/protocol.mjs`: API requests and queued synchronization.
- `public/app.mjs`, `public/index.html`, `public/styles.css`: application interface.
- `public/sw.js`: offline application-shell cache.
- `test-server.mjs`: HTTP/SQLite tests.
- `public/browser-tests.mjs`, `public/tests.html`: browser test harness.
- `verify.mjs`: API test recording and report generation.
- `verification.json`, `results/`, `figures/`, `report.html`: executed evidence and report.

## Extended verification and operations

Run `node complete.mjs` for the cache, crash/restart, backup/restore and deployment run-throughs. Read [completion-report.html](completion-report.html) and [OPERATIONS.md](OPERATIONS.md). All additional acceptance outcomes are in [completion.json](completion.json), with exact source hashes. `snapshot.mjs` and `restore.mjs` provide verified command-line recovery.
